Defining Participants - 6.3

Talend ESB Service Developer Guide

EnrichVersion
6.3
EnrichProdName
Talend Data Fabric
Talend Data Services Platform
Talend ESB
Talend MDM Platform
Talend Open Studio for ESB
Talend Real-Time Big Data Platform
task
Design and Development
Installation and Upgrade
EnrichPlatform
Talend ESB

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.

Updating the Service Provider

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&amp
;jndiConnectionFactoryName=ConnectionFactory&amp;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.

Service Provider Implementation

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);
    }
  ...
}

Updating the Service Consumer

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&amp
;jndiConnectionFactoryName=ConnectionFactory&amp;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());
        }
    }
    ... 
 }