Participants for the request-callback message exchange can be done in two ways: using the CXF API or using Spring-based configuration. It is possible to use org.talend.esb.mep.requestcallback.feature.CallContext methods for the participant setup.
The Provider side setup includes creating a Provider service (that receives the request from the consumer) and a callback client that will send the callback response to the consumer. The following is an example of a provider side service Spring configuration:
Provider side service Spring configuration
<jaxws:endpoint xmlns:library="http://services.talend.org/demos/Library/1.0" id="LibraryProviderJMS" address="jms:jndi:dynamicQueues/library.queue?jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory& ;jndiConnectionFactoryName=ConnectionFactory&jndiURL=tcp://localhost:61616" serviceName="library:LibraryProvider" endpointName="library:LibraryJmsPort" implementor="#libraryServerImpl"> <jaxws:features> <bean class="org.talend.esb.mep.requestcallback.feature.RequestCallbackFeature"/> <bean class="org.apache.cxf.feature.LoggingFeature"/> </jaxws:features> </jaxws:endpoint>
Warning
RequestCallbackFeature feature should be added to the service to allow request-callback functionality.
The service provider implementation may be done using JAX-WS. In such case, the provider side method in the annotated interface may look like this:
Library JAX-WS annotated interface
@WebService(targetNamespace = "http://services.talend.org/demos/Library/1.0", name = "Library") @XmlSeeAlso({org.talend.types.demos.library.common._1.ObjectFactory.class, org.talend.types.demos.generalobjects.errorhandling._1.ObjectFactory.class}) public interface Library { ... @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) @Oneway @WebMethod(action = "seekBookInBasement") public void seekBookInBasement( @WebParam(partName = "body", name = "SearchInBasementFor", targetNamespace = "http://types.talend.org/demos/Library/Common/1.0") org.talend.types.demos.library.common._1.SearchInBasementFor body ); ... }
A JAX-WS client must be created on the provider side to send a callback because the callback message is sent to a separate endpoint opened on the consumer side. The Client object may be set up as a jaxws:client bean in Spring configuration.
Provider side jaxws:client configuration example
<jaxws:client xmlns:library="http://services.talend.org/demos/Library/1.0" id="callbackResponseClient" serviceName="library:LibraryConsumer" endpointName="library:LibraryConsumerPort" address="jms://" serviceClass="org.talend.services.demos.library._1_0.LibraryConsumer"> <jaxws:features> <bean class="org.apache.cxf.feature.LoggingFeature"/> </jaxws:features> </jaxws:client>
The Client needs the Talend ESB CallContext feature to be set up as well as a service provider to work properly. CallContext class has a method that helps to set up callback proxy object. The method is called setupCallbackProxy and after it was applied to the proxy object, the proxy is ready to work.
Each request-callback message has a call context object associated with it. Call context is necessary for proper handling of request-callback messages. Call context object contains such information as call id, callback id, address of endpoint to which reply should be sent, and so on. The class org.talend.esb.mep.requestcallback.feature.CallContext is used to represent call context objects. It is necessary to extract call context from a message when handling request, at least to provide call id of the message to which callback message is related to.
In the example above, the callback proxy object was injected into the service provider and the callback was sent from the same thread in which request was received. In real-world scenarios, it is a rare situation, it is here just for the example purpose.
The request-callback processing on the provider side is shown below:
Request-Callback processing on provider side example
public class LibraryServerImpl implements Library { ... // Injected jaxws:client (see xml config above) private LibraryConsumer callbackResponseClient; @Resource private WebServiceContext wsContext; @Override public void seekBookInBasement(SearchInBasementFor body) { // Extract call context from message CallContext ctx = CallContext.getCallContext(wsContext.getMessageContext()); // Some business logic here ListOfBooks result = new ListOfBooks(); BookType book = new BookType(); result.getBook().add(book); PersonType author = new PersonType(); book.getAuthor().add(author); author.setFirstName("John"); author.setLastName("Stripycat"); Calendar dateOfBirth = new GregorianCalendar(202, Calendar.MAY, 17); author.setDateOfBirth(dateOfBirth.getTime()); book.getTitle().add("Hunting basement inhabitants"); book.getPublisher().add("Dusty Edition"); book.setYearPublished("2013"); // Set up necessary interceptors for callback proxy object ctx.setupCallbackProxy(callbackResponseClient); // Send callback message back to client callbackResponseClient.seekBookInBasementResponse(result); } ... }
The consumer side configuration includes setting up a CXF client to send a request to the provider, and setting up a CXF endpoint which will receive callback messages back from the provider. The client may be set up using Spring-based configuration. RequestCallbackFeature must be added to the client to work properly. The following is an example of Client Spring configuration:
CXF client configuration on consumer side
<jaxws:client id="library" serviceName="library:LibraryProvider" endpointName="library:LibraryJmsPort" address="jms:jndi:dynamicQueues/library.queue?jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory& ;jndiConnectionFactoryName=ConnectionFactory&jndiURL=tcp://localhost:61616" serviceClass="org.talend.services.demos.library._1_0.Library"> <jaxws:features> <bean class="org.talend.esb.mep.requestcallback.feature.RequestCallbackFeature"/> </jaxws:features> <jaxws:properties> <entry key="org.talend.esb.mep.requestcallback.CallbackEndpoint"> <ref bean="custTestServiceConsumerEndpoint"/> </entry> </jaxws:properties> </jaxws:client>
The setup of callback-receiving endpoint on the consumer side can be done via Spring configuration as well:
Callback-receiving endpoint Spring configuration
<jaxws:endpoint xmlns:library="http://services.talend.org/demos/Library/1.0" id="LibraryConsumerJMS" address="jms:jndi:dynamicQueues/libraryconsumer.queue?jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory&jnd iConnectionFactoryName=ConnectionFactory&jndiURL=tcp://localhost:61616" serviceName="library:LibraryConsumer" endpointName="library:LibraryConsumerPort" implementor="org.talend.services.demos.client.LibraryConsumerImpl"> <jaxws:features> <bean class="org.talend.esb.mep.requestcallback.feature.RequestCallbackFeature"/> <bean class="org.apache.cxf.feature.LoggingFeature"/> </jaxws:features> <jaxws:properties> <entry key="jaxws.provider.interpretNullAsOneway" value="true"/> </jaxws:properties> </jaxws:endpoint>
After both provider and consumer are set up, the request-callback message exchange can be done. The following is an example of sending a request:
Sending request from consumer to provider
public class LibraryTester { ... /** The Library proxy will be injected either by spring or by a direct call to the setter */ Library library; public void testRequestCallbackPositive() throws SeekBookError { // Create new request object and fill it with // some business data SearchInBasementFor request = new SearchInBasementFor(); request.getAuthorLastName().add("Stripycat"); // Add correlation info map to the request context Map<String, Object> rctx = ((BindingProvider) library).getRequestContext(); Map<String, Object> correlationInfo = new HashMap<String, Object>(); rctx.put(RequestCallbackFeature.CALL_INFO_PROPERTY_NAME, correlationInfo); // Send request to the provider library.seekBookInBasement(request); ... }
CallContext object related to request-callback messages contains various information regarding a message. The following code snippet shows how to get the message information on the consumer side when callback message is received.
Receiving callback message
@WebServiceProvider @Features(features = { "org.talend.esb.mep.requestcallback.feature.RequestCallbackFeature"}) public class LibraryConsumerImpl implements LibraryConsumer { @Resource private WebServiceContext wsContext; public void seekBookInBasementResponse(ListOfBooks body) { CallContext ctx = CallContext.getCallContext(wsContext.getMessageContext()); System.out.println("Info from CallContext:"); if (ctx == null) { System.out.println("- no CallContext"); } else { System.out.println("- Call ID is " + ctx.getCallId()); System.out.println("- Callback ID is " + ctx.getCallbackId()); } } ... }