This page tries to summarize summarizes an experience of implementing a new CXF transport.
|
h2. Use Cases
|
Normally implementation of custom transport is required to provide new physical protocol not yet supported by CXF(for instance udp, ftp). New CXF transport can be also a solution to support legacy ESB participants that have to be interoperable with JAX-WS services and clients. Actually CXF 2.3.x distribution provides transport implementation for following protocols: HTTP(S), JBI, JMS and Local(inside one JVM). Camel additionally implements CXF transport for camel exchanges.
|
Normally implementing a custom transport is required when providing a new physical protocol not yet supported by CXF (udp or ftp, for example). New CXF transports can be also a solution to support legacy ESB participants that have to be interoperable with JAX-WS services and clients. Presently the CXF 2.3.x distribution provides a transport implementation for the following protocols: HTTP(S), JBI, JMS and Local(inside one JVM). Camel additionally implements CXF transport for Camel exchanges.
|
h2. Architecture and Design
|
The transport functionality is based on two fundamental definitions: Conduit and Destination. Conduit is responsible to send a message to recipient and Destination – to receive a message from the sender. In order to send a response, Destination needs own back-channel Conduit (in case of request-response communication). Conduit and Destination are created by TransportFactory. CXF selects a correct TransportFactory based on transport URL. SOAP is also considered as high level transport and has own Conduit and Destination in CXF. To send message into physical channel Conduit should access the message context. Normal practice in this case is own implementation of OutputStream extending CachedOutputStream. The custom stream will be set to message and provides a possibility to access context in streaming or buffered way depending on transport requirements and to send message to physical channel. CachedOutputStream is configured to keep message in memory only up to limited size. If size is exceeded, message is swapped to disk.
|
The transport functionality is based on two fundamental definitions: conduit and destination. Conduits are responsible for sending a message to recipients and destinations for receiving a message from the sender. In order to send a response, a destination needs its own back-channel conduit (in case of request-response communication). Conduits and destinations are created by a TransportFactory. CXF selects the correct TransportFactory based on the transport URL. SOAP is also considered a high level transport and has its own conduit and destination in CXF. To send a message into a physical channel, the conduit should access the message context. Normal practice in this case is to use a subclass of OutputStream extending CachedOutputStream. The custom stream will be fed the message and provides a possibility to access context in streaming or buffered form depending on the transport requirements. CachedOutputStream is configured to keep message in memory only up to a predefined size. If this size is exceeded, the message is swapped to disk.
|
|
Class A class diagram of TransportFactory, Conduit, Destination and OutputStream is shown bellow:
|
!cxf-transport-class-diagram.jpg! h2. How it Works
|
Interaction between JAX-WS client and service using CXF transport is represented on in the following figure:
|
!cxf-transport-view.jpg! h3. Simplified Client Workflow:
|
* Step1: JAX-WS client invokes a service for example in this way:
|
* Step1: JAX-WS client invokes a service, in this manner for example:
|
{code:|borderStyle=solid} URL wsdlURL = this.getClass().getResource("/HelloWorld.wsdl");
|
...
|
String result = hw.sayHi(TEST_REQUEST); }} {code}
|
* Step2: CXF runtime selects the correct TransportFactory based on some criteria (described bellow)
|
* Step3: CXF runtime calls _TransportFactory.getConduit()_ method to obtain Conduit the conduit
|
* Step4: CXF runtime invokes _Conduit.prepare()_ and passes outgoing message as argument * Step5: Conduit sets own OutputStream (normally extended CachedOutputStream) as outgoing message content
|
* Step6: CXF runtime processes outgoing message, calls interceptors chain and invokes Conduit.close(Message) method is invoked for outgoing message.
|
* Step6: CXF runtime processes outgoing message, calls the interceptor chain and invokes Conduit.close(Message) method for the outgoing message.
|
* Step7: Finally, _OutputStream.doClose()_ method for the outgoing message is invoked
|
* Step8: In _doClose()_ method OutputStream class has access to marshalled outgoing message and exchange and will send this message through network to service using corresponded transport protocol
|
* Step8: In the _doClose()_ method, the OutputStream class has access to the marshalled outgoing message and exchange and will send this message to the service using the corresponding transport protocol
|
* Step9: In case of one-way communication exchange will be closed. Go Skip to sStep 14
|
* Step10: In case of request-response communication Conduit waits for service response in synchronies or asynchronies manner * Step11: When response is received, Conduit creates a new Message, set it’s context and puts it as In-Message in exchange as incoming message
|
* Step10: In case of request-response communication, the conduit will wait for the service response in synchronous or asynchronous manner * Step11: When response is received, the conduit creates a new message, sets its context and puts it as In-Message in the exchange as an incoming message
|
* Step12: When fault is received, Conduit creates a new Message, sets it’s context and puts it as fault message in exchange as in-fault message
|
* Step13: Conduit notifies incomingObesrver any incomingObserver about the response using _incomingObserver.onMessage()_ call
|
* Step14: _Conduit.close(Message)_ method is invoked for incoming message. Normally Conduit implementation decreases the reference or closes network connection with the service.
|
* Step14: _Conduit.close(Message)_ method is invoked for incoming message. Normally the conduit implementation decreases the reference count with the service, potentially closing the network connection if the count is zero.
|
* Step15: JAX-WS client code receives the response in sync or async style
|
h3. Simplified Service Workflow:
|
...
|
Endpoint.publish("udp://localhost:9000/hello", serverImpl); {code}
|
* Step2: CXF runtime selects correct TransportFactory based on some criteria (described bellow)
|
* Step3: CXF runtime calls _TransportFactory.getDestination()_ method to obtain Destination the destination
|
* Step4: As far as service is registered Ddestination will be activated using _Destination.activate()_ method
|
* Step5: _Destination.activate()_ opens network transport connections and listens to incoming requests
|
* Step6: When request comes, Destination creates message, sets content and notifies message observer getMessageObserver.onMessage(). Normally incoming connection is saved in correlation map to be extracted for appropriate response. * Step7: Service Implementation will be called with request message. In case of one-way communication it is end of exchange. In case of request-response business implementation returns response or throws fault exception.
|
* Step6: When a request comes, the destination creates a message, sets the content and notifies any message observers via getMessageObserver.onMessage(). Normally an incoming connection is saved in a correlation map to be extracted for the appropriate response. * Step7: The service implementation will be called with the request message. In case of one-way communication the exchange is now finished. In case of request-response, the business implementation either returns a response or throws a fault exception.
|
* Step8: The CXF Runtime requests a back-channel conduit from the destination via _Destination.getInbuiltBackChannel()_
|
* Step9: The Back-channel conduit's _prepare()_ method will be called with a response message as argument
|
* Step10: Back-channel conduit sets its own OutputStream as a message context
|
* Step11: CXF runtime processes response message, calls interceptors chain and invokes _Conduit.close(Message)_ method is invoked for response message.
|
* Step11: CXF runtime processes the response message, calls the interceptor chain and invokes _Conduit.close(Message)_ for the response message.
|
* Step12. Finally _OutputStream.doClose()_ method for the response message is invoked
|
* Step13: In _doClose()_ method the OutputStream class has access to the marshalled response message and will send this message through the network as a response to the client. Appropriate incoming connection normally is extracted from correlation map.
|
h2. Registration of Transport Factory
|
There are two ways to register transport factory: programmatic and programmatically or via sSpring configuration.
|
To register transport factory programmatically it is necessary to execute the following code:
|
{code:|borderStyle=solid} Bus bus = BusFactory.getThreadDefaultBus();
|
...
|
extension.registerConduitInitiator("http://cxf.apache.org/transports/TRANSPORT_PREFIX/configuration", customTransport); {code}
|
Where TRANSPORT_PREFIX is the protocol of the new transport (http, https, jms, udp).
|
|
Alternatively it is also possible to register transport factory using spring configuration:
|
For Spring configuration, the following could be used instead:
|
{code:xml} <bean class="org.company.cxf.transport.CustomTransportFactory"
|
...
|
</bean> {code}
|
To define new transport endpoint in WSDL document it is necessary:
|
To define a new transport endpoint in the WSDL document follow these two steps:
|
a) Set the soap:binding transport attribute to the transport URL value (http://cxf.apache.org/transports/TRANSPORT_PREFIX)
|
b) The Port address element should be bound to namespace equals to transport URL in WSDL XML
|
Sample:
|
...
|
h2. Conduit and Destination Lifecycle
|
Conduit and Destination lifecycle is requested from TransportFactory by every client or service creation. TransportFactory can either create Conduit and Destination for each request or cache them based on service endpoint information
|
The conduit and destination lifecycle can be started by the TransportFactory during every client or service creation. The TransportFactory can either create a conduit and destination for each request or cache them based on service endpoint information.
|
h2. Concurrency Aspects
|
Conduit and Ddestination objects can by concurrently accessed by multiple threads. Implementations should care about concurrent correlations maps and/or synchronization primitives.
|
h2. References
|
...
|