Request Response Binding

OASIS

There is now a resolved issue relating to databinding and operation selection in the OASIS assembly TC

http://www.osoa.org/jira/browse/ASSEMBLY-79
http://www.osoa.org/jira/browse/BINDINGS-27
http://www.osoa.org/jira/browse/BINDINGS-43
http://www.osoa.org/jira/browse/BINDINGS-44
http://www.osoa.org/jira/browse/BINDINGS-45

The bindings TC has yet to define precisely what wire formats and operation selectors will be supported by each binding but we can imagine the kind of thing that will be required in order to investigate the implementation implications. Here I'm using JMS as an exemplar based on the notes from Ant and Raymond.

Binding invocation chain proposal


 

Binding provider proposal

JMS Exemplar

Wire Format Scenarios

Based on the message types that JMS supports and the mapping from the SCA JMS binding specification. Note the wire formats here are just guesses based on what Ant came up with. I've seen no spec activity on this yet.

#

JMS Message Type

JMS Message Body

Service interface

ImplemenationInterface

WireFormat

OperationSelection

Notes

1

JMS Text

XML

WSDL

doSomething(SDO/POJO)

<wireFormat.jmsTextXML/>

operationSelection.jmsDefault

From the spec

2

JMS Bytes

XML

WSDL

doSomething(SDO/POJO)

<wireFormat.jmsBytesXML/>

operationSelection.jmsDefault

From the spec

3

Any JMS Message

any

doSomething(javax.jms.Message)

doSomething(javax.jms.Message)

<wireFormat.jmsTextXML/>

operationSelection.jmsDefault

From the spec

4

Any JMS Message

any

Custom

doSomething(SDO/POJO)

<wireFormat.someCustomFormat />

operationSelection.jmsDefault

need a custom body extractor/inserter in this case

5

JMS Text

characters

doSomething(String)

doSomething(String)

<wireFormat.jmsText/>

operationSelection.jmsDefault

 

6

JMS Bytes

bytes

doSomething(byte[])

doSomething(byte[])

<wireFormat.jmsBytes/>

operationSelection.jmsDefault

 

7

JMS Object

Java Object

doSomething(SDO or POJO)

doSomething(SDO or POJO)

<wireFormat.jmsObject/>

operationSelection.jmsDefault

 

8

JMS Map

?

?

?

<wireFormat.jms.map/>

 

 

9

JMS Stream

?

?

?

<wireFormat.jms.stream/>

 

 

wireFormat.jmsTextXML

This is the default format, you don't need to specify it.

Composite

A composite using the default wire format (JMS text messages containing XML).

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://helloworld"
	xmlns:hw="http://helloworld"
    name="helloworld">

    <component name="HelloWorldReferenceComponent">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldReferenceImpl" />
        <reference name="helloWorldService1" >
            <binding.jms>
                <destination name="HelloWorldService1"/>
                <wireFormat.jmsTextXML/>
            </binding.jms>
        </reference>      
    </component>
    
    <component name="HelloWorldServiceComponent1">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldServiceImpl" />
        <service name="HelloWorldService">
            <binding.jms>
                <destination name="HelloWorldService1"/>
            </binding.jms>
        </service>
    </component>     
</composite>

Java service interface

@Remotable
public interface HelloWorldService {

    public String getGreetings(String name);
    
    public String getPersonGreetings(Person person);
}
public class Person {
  String firstName;
  String lastName;
  
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  
  public String getLastName() {
    return lastName;
  }
  
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

WSDL Service Interface

    <wsdl:types>
        <schema elementFormDefault="qualified" targetNamespace="http://helloworld" xmlns="http://www.w3.org/2001/XMLSchema">

            <xsd:complexType name="PersonType">
                <xsd:sequence>
                    <xsd:element name="firstName" type="xsd:string"/>
                    <xsd:element name="lastName" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
            
            <element name="getPersonGreetings">
                <complexType>
                    <sequence>
                        <element name="person" type="PersonType"/>
                    </sequence>
                </complexType>
            </element> 
            
            <element name="getPersonGreetingsResponse">
                <complexType>
                    <sequence>
                        <element name="getPersonGreetingsReturn" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>
            
            <element name="getGreetings">
                <complexType>
                    <sequence>
                        <element name="name" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>

            <element name="getGreetingsResponse">
                <complexType>
                    <sequence>
                        <element name="getGreetingsReturn" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>
            
        </schema>
    </wsdl:types>

    <wsdl:message name="getGreetingsRequest">
        <wsdl:part element="tns:getGreetings" name="parameters"/>
    </wsdl:message>

    <wsdl:message name="getGreetingsResponse">
        <wsdl:part element="tns:getGreetingsResponse" name="parameters"/>
    </wsdl:message>
    
    <wsdl:message name="getPersonGreetingsRequest">
        <wsdl:part element="tns:getPersonGreetings" name="parameters"/>
    </wsdl:message>

    <wsdl:message name="getPersonGreetingsResponse">
        <wsdl:part element="tns:getPersonGreetingsResponse" name="parameters"/>
    </wsdl:message>    

    <wsdl:portType name="HelloWorld">
        <wsdl:operation name="getGreetings">
            <wsdl:input message="tns:getGreetingsRequest" name="getGreetingsRequest"/>
            <wsdl:output message="tns:getGreetingsResponse" name="getGreetingsResponse"/>
        </wsdl:operation>
        <wsdl:operation name="getPersonGreetings">
            <wsdl:input message="tns:getPersonGreetingsRequest" name="getPersonGreetingsRequest"/>
            <wsdl:output message="tns:getPersonGreetingsResponse" name="getPersonGreetingsResponse"/>
        </wsdl:operation>        
    </wsdl:portType>

getGreetings message body

<name>Fred Bloggs</name>

<getGreetingsReturn>Hello Fred Bloggs</getGreetingsReturn>

For consistency you would imagine the following but this is not what I think the spec says.

<getGreetings>
<name>Fred Bloggs</name>
</getGreetings>

<getGreetingsResponse>
<getGreetingsReturn>Hello Fred Bloggs</getGreetingsReturn>
</getGreetingsReponse>

getPersonGreetings message body

<getPersonGreetings>
<firstName>Fred</firstName>
<lastName>Bloggs</lastName>
</getPersonGreetings>

<getPersonGreetingsResponse>
<getPersonGreetingsReturn>Hello Fred Bloggs</getPersonGreetingsReturn>
</getPeronGreetingsReponse>

Resulting runtime configuration

Interceptor

Interceptors implement the Invoker interface which is as follows:

public interface Invoker {

    /**
     * Process a synchronous wire
     *
     * @param msg The request Message for the wire
     * @return The response Message from the wire
     */
    Message invoke(Message msg);

}

Interceptors are placed on a wire in the order dictated by their phase

WireFormatJMSTextXMLReferenceInterceptor

public class WireFormatJMSTextXMLReferenceInterceptor implements Interceptor {

    private Invoker next;
    private RuntimeWire runtimeWire;
    private JMSResourceFactory jmsResourceFactory;
    private JMSBinding jmsBinding;
    private JMSMessageProcessor requestMessageProcessor;
    private JMSMessageProcessor responseMessageProcessor;


    public WireFormatJMSTextXMLReferenceInterceptor(JMSBinding jmsBinding, JMSResourceFactory jmsResourceFactory, RuntimeWire runtimeWire) {
        super();
        this.jmsBinding = jmsBinding;
        this.runtimeWire = runtimeWire;
        this.jmsResourceFactory = jmsResourceFactory;
        this.requestMessageProcessor = JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding);
        this.responseMessageProcessor = JMSMessageProcessorUtil.getResponseMessageProcessor(jmsBinding);
    }

    public Message invoke(Message msg) {
        if (jmsBinding.getRequestWireFormat() instanceof WireFormatJMSTextXML){
            msg = invokeRequest(msg);
        }
        
        msg = getNext().invoke(msg);
        
        if (jmsBinding.getResponseWireFormat() instanceof WireFormatJMSTextXML){
            msg = invokeResponse(msg);
        }
        
        return msg;
    }
    
    public Message invokeRequest(Message msg) {
        try {
            // get the jms context
            JMSBindingContext context = (JMSBindingContext)msg.getHeaders().get(JMSBindingConstants.MSG_CTXT_POSITION);
            Session session = context.getJmsSession();
            
            javax.jms.Message requestMsg = requestMessageProcessor.insertPayloadIntoJMSMessage(session, msg.getBody());
            msg.setBody(requestMsg);
            
            requestMsg.setJMSReplyTo(context.getReplyToDestination());
            
            return msg;
        } catch (JMSException e) {
            throw new JMSBindingException(e);
        } 
    }
    
    public Message invokeResponse(Message msg) {
        if (msg.getBody() != null){
            Object[] response = (Object[])responseMessageProcessor.extractPayloadFromJMSMessage((javax.jms.Message)msg.getBody());
            if (response != null && response.length > 0){
                msg.setBody(response[0]);
            } else {
                msg.setBody(null);
            }
        }

        return msg;
    }    

    public Invoker getNext() {
        return next;
    }

    public void setNext(Invoker next) {
        this.next = next;
    }    
}

WireFormatJMSTextXMLServiceInterceptor

public class WireFormatJMSTextXMLServiceInterceptor implements Interceptor {
    private Invoker next;
    private RuntimeWire runtimeWire;
    private JMSResourceFactory jmsResourceFactory;
    private JMSBinding jmsBinding;
    private JMSMessageProcessor requestMessageProcessor;
    private JMSMessageProcessor responseMessageProcessor;

    public WireFormatJMSTextXMLServiceInterceptor(JMSBinding jmsBinding, JMSResourceFactory jmsResourceFactory, RuntimeWire runtimeWire) {
        super();
        this.jmsBinding = jmsBinding;
        this.runtimeWire = runtimeWire;
        this.jmsResourceFactory = jmsResourceFactory;
        this.requestMessageProcessor = JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding);
        this.responseMessageProcessor = JMSMessageProcessorUtil.getResponseMessageProcessor(jmsBinding);
    }
    
    public Message invoke(Message msg) {

        if (jmsBinding.getRequestWireFormat() instanceof WireFormatJMSTextXML){
            msg = invokeRequest(msg);
        }
        
        msg = getNext().invoke(msg);
        
        if (jmsBinding.getResponseWireFormat() instanceof WireFormatJMSTextXML){
            msg = invokeResponse(msg);
        }
        
        return msg;
    }

    public Message invokeRequest(Message msg) {
        // get the jms context
        JMSBindingContext context = (JMSBindingContext)msg.getHeaders().get(JMSBindingConstants.MSG_CTXT_POSITION);
        javax.jms.Message jmsMsg = context.getJmsMsg();
        
        if ("onMessage".equals(msg.getOperation().getName())) {
            msg.setBody(new Object[]{jmsMsg});
        } else {
            Object requestPayload = requestMessageProcessor.extractPayloadFromJMSMessage(jmsMsg);
            msg.setBody(requestPayload);
        }
                
        return msg;
    }
    
    public Message invokeResponse(Message msg) {
        
        // get the jms context
        JMSBindingContext context = (JMSBindingContext)msg.getHeaders().get(JMSBindingConstants.MSG_CTXT_POSITION);
        javax.jms.Message requestJMSMsg = context.getJmsMsg();
        Session session = context.getJmsSession();

        javax.jms.Message responseJMSMsg;
        if (msg.isFault()) {
            responseJMSMsg = responseMessageProcessor.createFaultMessage(session, (Throwable)msg.getBody());
        } else {
            responseJMSMsg = responseMessageProcessor.insertPayloadIntoJMSMessage(session, msg.getBody());
        }
    
        msg.setBody(responseJMSMsg);
        
        return msg;
    }    

    public Invoker getNext() {
        return next;
    }

    public void setNext(Invoker next) {
        this.next = next;
    }
}

3 - wireFormat.? with onMessage(javax.jms.Message msg) style interface

This format can be derived from the service interface so again you don't need to specify it.

Composite

A composite using any form of JMSMessage end to end.

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://helloworld"
	xmlns:hw="http://helloworld"
    name="helloworld">

    <component name="HelloWorldReferenceComponent">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldReferenceImpl" />
        <reference name="helloWorldService1" >
            <binding.jms>
                <destination name="HelloWorldService1"/>
                <wireFormat.jms/>
            </binding.jms>
        </reference>      
    </component>
    
    <component name="HelloWorldServiceComponent1">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldServiceImpl" />
        <service name="HelloWorldService">
            <binding.jms>
                <destination name="HelloWorldService1"/>
            </binding.jms>
        </service>
    </component>     
</composite>

Java service interface

@Remotable
public interface HelloWorldService {

    public void setGreetings(javax.jms.Message msg);
}

Problem with this is that it's an interface and JAXB doesn't work with interfaces.

WSDL service interface

    <wsdl:types>
        <schema elementFormDefault="qualified" targetNamespace="http://helloworld" xmlns="http://www.w3.org/2001/XMLSchema">
            
            <element name="setGreetings">
                <complexType>
                    <sequence>
                        <any/>
                    </sequence>
                </complexType>
            </element>
            
        </schema>
    </wsdl:types>

    <wsdl:message name="setGreetingsRequest">
        <wsdl:part element="tns:setGreetings" name="parameters"/>
    </wsdl:message>
  

    <wsdl:portType name="HelloWorld">
        <wsdl:operation name="setGreetings">
            <wsdl:input message="tns:setGreetingsRequest" name="setGreetingsRequest"/>
        </wsdl:operation>    
    </wsdl:portType>

getGreetings message body

Fred Bloggs

Resulting runtime configuration

TBD

JMSReferenceInterceptor

A pass though. JMS message is delivered from the implementation to the binding.

JMSServiceInterceptor

A pass through. JMS message is delivered from the binding to the implementation.

4 - wireFormat.someCustomFormat

Composite

A composite using any form of JMS message on the wire but with a non-JMS message business interface

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://helloworld"
	xmlns:hw="http://helloworld"
    name="helloworld">

    <component name="HelloWorldReferenceComponent">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldReferenceImpl" />
        <reference name="helloWorldService1" >
            <binding.jms>
                <destination name="HelloWorldService1"/>
                <wireFormat.someCustomFormat/>
            </binding.jms>
        </reference>      
    </component>
    
    <component name="HelloWorldServiceComponent1">
        <implementation.java class="org.apache.tuscany.sca.binding.jms.format.jmstextxml.helloworld.HelloWorldServiceImpl" />
        <service name="HelloWorldService">
            <binding.jms>
                <destination name="HelloWorldService1"/>
                <wireFormat.someCustomFormat/>
            </binding.jms>
        </service>
    </component>     
</composite>

Java service interface

@Remotable
public interface HelloWorldService {

    public String getGreetings(String name);
    
    public String getPersonGreetings(Person person);
}
public class Person {
  String firstName;
  String lastName;
  
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  
  public String getLastName() {
    return lastName;
  }
  
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

Resulting runtime configuration

TBD

MyMessageReferenceInterceptor(Would be custom name)

Take service interface arguments from tuscany message and perform custom transformation to create JMSmessage

MyMessageServiceInterceptor

Take JMSTextMessage body and perform custom transformation create service interface arguments in tuscany message

Background Material

Current Situation

Message Formats

Plugability Options

A note from Raymond

I guess the key is to define the extensibility on the edges of invocation chains. For reference binding or component implementation, the invoker is
transforming the SCA messages into native messages (such as JMSMessage, JCA CCI Record, or SOAPEnvelope). For service binding, the binding listener is
responsible for bridging the native messages into SCA messages. In the middle, we have the invocation chains that consist of Interceptors that deal
with SCA messages only. The pipelines are illustrated below.

Reference Binding and Implementation
Data: ----- SCA Message ------- ---------------------------------- Native Message ---------------------------------------------------------------------------------------
Invocation: Interceptor...Interceptor... Invoker ... (Some code to deal with the transformation from SCA message to binding/implementation specific message)

Service Binding
Data: – Native Message ---------------------------------------------------------------------------------------------------------------------------------------- – SCA Message —
Invocation: Binding Listener... ... (Some code to deal with the transformation from SCA message to binding/implementation specific message)...Interceptor ... Invoker

One idea is that we could wrap the native message into SCA message with the body set to the native message. This way, we can just contribute Inteceptors
to deal with the native messages. For example, in the JMSBinding case, we could have FunctionSelector (maps the JMS message to an operation) and
MessageMapper (unpack/pack the JMSMessage from/to SCA payload). The inteceptors that deal with native messages can be positioned to phases next
to the bindings. The new picture can be:

---------------------- native JMS age ----------------- --------------------- SCA message with normalized payload ----------------
JMSListener -> FunctionSelector -> MessageMapper --> Other Interceptors ..............................................................................

— SCA Message — ---------------------- native JMS Message ----------------
MessageMapper --> JMS Binding Invoker

Ant's JMS examples

Scenarios

The JMS binding needs to support sending/receiving different types of JMS messages and payloads, for example,

1- JMS Text messages containing XML (this is the default type of messages)
2- JMS Object messages containing the actual request/response Objects
3- JMS Text messages containing a String, how the payload String is obtained could be a default (toString on the args) or a user plugable function
4- Arbitrary JMS message types with arbitrary payloads and a user customizable handler to convert to/from the JMS message

Note, that the latest spec drafts say the default may be either a JMS Text message or a JMS Bytes message. The response should probably match what was received so the response and callback handler need some way of knowing what type of request message was received.

For the non-default cases we need a way of defining this somewhere. Currently it uses a Tuscany specific attribute on the JMS binding - "messageProcessor", latest spec discussions are for a new wireFormat SCDL element - http://lists.oasis-open.org/archives/sca-bindings/200808/msg00036.html

We could have the following wireFormat extensions to support those above 4 cases:
1- <wireFormat.jmstextxml/> (the default, you wouldn't need to specify it)
2- <wireFormat.jmsobject/>
3- <wireFormat.jmstext/> and <wireFormat.jmstext handler="some.user.Class"/>
4- <wireFormat.jmscustom handler="some.user.Class"/>

A problem with 3 and 4 would be what is the interface for the handler class? To create a JMS Message requires a JMS session so the handler cannot easily create the JMS message itself so its probably easier if the type of message can be determined from the wireFormat SCDL.

A complete example of using JMS Object messages could be:

<composite ...>

    <component name="Client">
        <implementation.java class="..."/>
        <reference name="serviceA">
           <binding.jms uri="jms:TheService">
              <wireFormat.jmsobject/>
           </binding.jms>
        </reference>
    </component>

    <component name="Service">
        <implementation.java class="..."/>
        <service name="TheService">
            <binding.jms />
              <wireFormat.jmsobject/>
            </binding.jms>
        </service>
    </component>

</composite>