This page summarizes an experience and use cases of implementing a new custom CXF transport.

Use Cases

Basically there are two main use cases for implementing a new CXF transport:

  1. Providing a new physical protocol not yet supported by CXF (udp or ftp, for example). Some such cases can be solved by using integration with a corresponding Camel component, but if no such component is available or workable creating a new custom CXF transport should be considered.
  2. Supporting tight integration with another framework (like JBI or Camel). In this case integration would be kept transparent for CXF applications - they would just speak directly with the target framework on the transport level. Here, the Transport implementation would be responsible for converting and transferring CXF exchange, messages and faults to target framework.

Presently the CXF distribution provides a transport implementation for the following protocols: HTTP(S), JBI, JMS and Local(inside one JVM). Camel additionally implements a CXF transport for Camel exchanges.

Architecture and Design

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 would be to use a subclass of OutputStream extending CachedOutputStream. The custom stream will be fed the message and provided the 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.

A class diagram of TransportFactory, Conduit, Destination and OutputStream is shown below:

How it Works

Interaction between JAX-WS client and service using CXF transport is represented in the following figure:

Simplified Client Workflow:

Simplified Service Workflow:

Registration of Transport Factory

There are two ways to register a transport factory: programmatically or via Spring configuration.
To register transport factory programmatically it is necessary to execute the following code:

     Bus bus = BusFactory.getThreadDefaultBus();
     DestinationFactoryManagerImpl dfm = bus.getExtension(DestinationFactoryManagerImpl.class);
     CustomTransportFactory customTransport = new CustomTransportFactory();
     dfm.registerDestinationFactory(TRANSPORT_IDENTIFIER, customTransport);

     ConduitInitiatorManager extension = bus.getExtension(ConduitInitiatorManager.class);
     extension.registerConduitInitiator(TRANSPORT_IDENTIFIER, customTransport);

TRANSPORT_IDENTIFIER is unique transport id (normally in form “http://apache.org/transports/PROTOCOL_PREFIX”).

For Spring configuration, the following could be used instead:

	<bean class="org.company.cxf.transport.CustomTransportFactory"
		lazy-init="false">
		<property name="transportIds">
			<list>
			  <value>TRANSPORT_IDENTIFIER</value>
			</list>
		</property>
	</bean>

TransportFactory selection

As far as binding TransportFactory is found, CXF looking for protocol TransportFactory responsible for physical network communication. In this case important is method TransportFactory.getUriPrefixes(). This method returns list of protocol prefixes supported by this TransportFactory.
When CXF client or service try to communicate using URL with specified protocol prefix (http://, https://, jms://, local://), CXF looks into registered transport factories map and gets the right one for this prefix. If no TransportFactory for this protocol is found, CXF throws corresponded exception.

Client configuration:

<jaxws:client id="FlightReservationClient"
	xmlns:serviceNamespace="http://www.apache.org/cxf/samples/FlightReservation"

serviceClass="org.apache.cxf.samples.flightreservation.FlightReservation"
	serviceName="serviceNamespace:FlightReservationService" endpointName="serviceNamespace:FlightReservationSOAP">
	address="http://localhost:8040/services/FlightReservationService">
</jaxws:client>
…

TransportFactory class:

…
    private static final Set<String> URI_PREFIXES = new HashSet<String>();
    static {
        URI_PREFIXES.add("http://");
        URI_PREFIXES.add("https:");
    }    
    public Set<String> getUriPrefixes() {
        return URI_PREFIXES;
    }

Conduit and Destination Lifecycle

Destinations are normally created by service on startup and released by shutdown. Conduits can be either recreated for each request or cached based on endpoint information for whole client life time. Clients can make concurrent calls to endpoints using different protocols and bound them to different conduits.

Concurrency Aspects

Conduit and destination objects can by concurrently accessed by multiple threads. Implementations should care about thread safety of the class.

Streaming

It is strongly recommended to don’t break streaming in Conduit and Destination implementations, if physical protocol supports it. CXF is completely streaming oriented – it causes high performance and scalability.

References

What is the start point to understand the CXF transport layer and implement own transport? It makes sense to read CXF documentation CXF transports overview and analyze source code of existing CXF transports (Local and JMS once are more straightforward). They are located into packages: org.apache.cxf.transport.local and org.apache.cxf.transport.jms correspondingly.