Apache CXF Software Architecture Guide
This document provides an architectural overview of the Apache CXF services framework.
Table of Contents
Architectural Goals and Constraints
The Apache CXF services framework seeks to build the necessary infrastructure components for services. Goals for CXF are many and include:
- Embeddable
- High performance
- Easy configuration
- Intuitive and easy to use
- Clean separation of front-ends from the core code
- Data formats support
- Data bindings support
- Protocol bindings support
- Multiple transports support
- Multiple Programming Languages Support
- WS-* and related specifications support
- Tools for code generation and WSDL validation
- Flexible deployment
CXF-API
The overall CXF architecture is primarily made up of the following parts:
- Bus: Contains a registry of extensions, interceptors and Properties
- Front-end: Front-ends provide a programming model to create services.
- Messaging & Interceptors: These provide the low level message and pipeline layer upon which most functionality is built.
- Service Model: Services host a Service model which is a WSDL-like model that describes the service.
- Pluggable Data Bindings: ...
- Protocol Bindings: Bindings provide the functionality to interpret the protocol.
- Transports: Transportfactory creates Destinations (Receiving) and Conduits (Sending)
In the upcoming sections, we'll take a look at each layer in turn and examine how they work together.
Bus
The bus, being CXF's backbone, is a provider of shared resources to the CXF runtime. Examples for such shared resources include WSDL managers and binding factory managers. The bus can easily be extended to include your own custom resources or services, or you can replace default resources like the HTTP destination factory (based on Jetty) with your own (possibly based on another web container such as Apache Tomcat).
This extensibility is made possible by dependency injection; the default bus implementation is based on Spring Framework, which wires the runtime components together for you.
The SpringBusFactory
searches for all bean configuration files in the META-INF/cxf
directories on your classpath, and builds an application context from them. The bean configuration files included in the application context construction are:
META-INF/cxf/cxf.xml
(e.g., in cxf-core
only)META-INF/cxf/cxf-extension.xml
(e.g. in cxf-rt-bindings-soap
)META-INF/cxf/cxf-property-editors.xml
(e.g. in cxf-rt-transports-http
)
See Configuration of the Bus for an example of how to customize the bus by supplying your own bean configuration file and Configuration of Runtime Constructed Objects for more information on the special case of injecting into objects created by the runtime (as opposed to objects created by the IoC container itself).
How service calls are processed
Client Side
Server Side
Front-ends
Front-ends provide a programming model to interact with CXF. JAX-WS, JAX-RS, Simple and Javascript front-end APIs are provided by CXF . Each implementation is cleanly separated from the rest of CXF, just like the bindings and the core. Front-ends provide functionality through interceptors that are added to Services and Endpoints. See also Front-ends
Messaging & Interceptors
CXF is built on a generic messaging layer comprised of Messages, Interceptors, and InterceptorChains. Interceptors are the fundamental unit of functionality. By dividing up how messages are processed and sent, this gives CXF a very flexible architecture. It can be reconfigured at any point in the processing. This also gives CXF the ability to pause & resume interceptor chains.
Interceptors have a method, handleMessage
, which allows them to act on the Message.These Interceptors can then be built up into chains of interceptors, straightforwardly called InterceptorChains. Some examples include:
- An interceptor which parses just the headers of a SOAP message into DOM elements
- A WS-Security interceptor which decrypts or authenticates an incoming message.
- An outgoing data binding interceptor which serializes the result
Interceptors are uni-directional and are inherently unaware of whether they are dealing with a request, response, or fault.
Phase Interceptors
CXF provides an InterceptorChain
implementation called the PhaseInterceptorChain
. When Interceptors are added to the chain, they are grouped into ordered phases. A PhaseInterceptor
may provide guidance as to how it is to be ordered within the phase.
Let us take a hypothetical simplified example (NOTE: these phases and interceptors don't necessarily exist in CXF). Let us say we are parsing a SOAP message. We may want to have two phases. First, a dispatch phase which parses the soap headers and determines which service to route the Message to. Second, an unmarshal phase which binds the SOAP body to JAXB objects. In the first dispatch phase we could implement this via two interceptors, first a ReadHeadersInterceptor which parses the headers and second a WS-AddressingInInterceptor which determines which service we're invoking from the WS-Addressing header. In the second unmarshal phase, we have just a single JAXBUnmarshallerIntercptor. Both the ReadHeadersInterceptor
and AddressingInInterceptor
would tell the PhaseInterceptorChain
they are in the "dispatch" phase by returning "dispatch" when getPhase()
is called. Additionally, the ReadHeadersInterceptor
could specify that it wants to run before the AddressingInInterceptor
by returning the interceptor id when Interceptor.getBefore()
is called.
Before it was mentioned how chains were very dynamic and flexible. In our above example, we could add interceptors specific to that service once it is resolved. Or we could pause the chain once while we wait for some external chain, like an asynchronous service response.
Fault Handling
At any point during processing, an interceptor may throw a Fault, or a derivative of a Fault like the SoapFault
. This will cause the chain to stop invoking and unwind it. Unwinding consists of calling handleFault on each interceptor that was invoked in reverse order.
InterceptorChains have the concept of a fault observer. Once the chain is unwound, the fault interceptor is invoked with the message that caused the fault. The fault observer may trigger a new chain which then invokes a specified set of interceptors meant to handle faults.
Exchanges
In addition to the concept of a Message, there is the concept of the Exchange
. The exchange class holds a references to the in, out and fault messages for the current message exchange.
It also holds properties specific to the exchange, and not just the message. For instance the Exchange
holds the Service
that is current being invoked in it.
Reentrant InterceptorChains
An interesting feature of the PhaseInterceptorChain
is that it is reentrant. This can be powerful and slightly dangerous. This feature is only used in CXF during the sending of an outgoing message, The SoapOutInterceptor
is the best example:
public void handleMessage(Message m) {
writeSoapEnvelopeStart();
writeSoapBodyStart();
// invoke next interceptor, which writes the contents of the SOAP Body
m.getInterceptorChain().doIntercept(m);
writeSoapBodyEnd();
writeSoapEnvelopeEnd();
}
The Service Model
The Service model is the representation of a service within CXF. It is made up of two parts. First there is the ServiceInfo
which contains a WSDL-like model of the service and its operations, bindings, endpoints and schema. Second, there is the Service itself, which contains the ServiceInfo
, data-binding information, service interceptors, service properties and more.
A service can be constructed from many different sources including classes and WSDLs (1.1 or 2.0). Typically front-ends are responsible for creating a service via service factories. Factory components such as ServerFactoryBean
and ClientProxyFactoryBean
can used with the front-end to create, publish and consume web services. The factory classes build up the service model and configure the service interceptors, data bindings and more.
The Service model itself is contained in the ServiceInfo
class. The following image depicts a subset of the Service Model's packaged API:
Data Bindings
Data bindings implement the mapping between XML elements and Java objects. Data bindings convert data to and from XML, produce XML schema, and provide support for wsdl2java code generation. Not all data bindings support all of this functionality. At very least, a data binding must provide the data conversion. See Data Binding Architecture for details. Currently supported data bindings include JAXB 2.x (default) and Aegis.
Protocol Bindings
Bindings provide ways to map concrete formats and protocols on top of transports. A binding contains two main parts, a BindingFactory
and a Binding
. A BindingFactory
builds a Binding
from the service model's BindingInfo
. The binding contains interceptors specific to the binding and also implements the createMessage()
method, which creates a Message
implementation specific for that binding.
CXF currently supported the following bindings protocols: SOAP 1.1, SOAP 1.2, REST/HTTP, pure XML and CORBA.
The Soap Binding
The prototypical binding is SOAP. It has its own Message
class called the SoapMessage
. It adds the ability to hold the current SoapVersion
and the headers for the message.
The Soap
binding also adds a special type of interceptor called the SoapInterceptor
. The SoapInterceptor
adds two methods to the Interceptor
class:
Set<URI> getRoles();
Set<QName> getUnderstoodHeaders();
These inform the SOAP interceptors as to what headers and roles the particular SOAP interceptor understands.
It has many interceptors designed to handle SOAP messages:
StaxInInterceptor
: Creates an XMLStreamReader
from an incoming InputStream
ReadHeadersInterceptor
: Reads the headers into the SoapMessage
MustUnderstandInterceptor
: Checks the MustUnderstand
attributes of all the headers against all the SoapInterceptor
's getUnderstoodHeaders
method.SoapOutInterceptor
:
Additional Bindings
Other bindings include REST/HTTP binding, pure XML binding, and the CORBA binding.
Transports
CXF includes its own transport abstraction layer to hide transport specific details from the binding and front end layers. Currently supported transports include: HTTP, HTTPs, HTTP-Jetty, HTTP-OSGI, Servlet, local, JMS, In-VM and many others via the Camel transport for CXF such as SMTP/POP3, TCP and Jabber. Learn more about transports here.
Conduits
Conduits provide the basis for outgoing message sending. A Conduit
is created from a ConduitInitiator
. Sending a message is a multistep pocess:
- Call conduit.prepare(message): this starts the message sending. At this point a
Conduit
may initiate a connection and set the OutputStream for the outgoing message. - Writing of the actual message to the
OutputStream
- Call to
conduit.close(message)
: this closes and disposes of any existing resources for the message sending.
A message sender may also register a MessageObserver
with the Conduit. If the Conduit
is synchronous, the MessageObserver
will be notified once a response has been received.
Destinations
Destinations are the basis for receiving incoming messages. A destination is created from a DestinationFactory
:
DestinationFactoryManager dfManager = bus.getExtension(DestinationFactoryManager.class);
// Find a DestinationFactory for the SOAP HTTP transport
DestinationFactory df = dfManager.getDestinationFactory("http://schemas.xmlsoap.org/wsdl/soap/http");
// TODO: outline building of EndpointInfo
EndpointInfo endpointInfo = ...;
Destination destination = df.getDestination(endpointInfo);
MessageObservers can then be registered with Destinations. These listen for incoming messages:
MessageObserver myObserver = ...;
destination.setMessageObserver(myObserver);
The most common MessageObserver
used in CXF is the ChainInitiationObserver
. This takes the incoming message, creates a message Exchange & PhaseInterceptorChain
, then starts the chain.
A JAX-WS example
Here's a small example of what might happen when we publish a service via the JAX-WS Endpoint.publish()
method.
- Call to
Endpoint.publish("http://localhost/service", myService)
- The
EndpointImpl
creates a Service from the myService
object using the JaxWsServiceFactoryBean
using the class and/or WSDL - An
EndpointInfo
is created for the Endpoint.publish
URL - A
JaxWsEndpointImpl
is created from the EndpointInfo
. This contains the JAX-WS endpoint specific interceptors - The
JaxWsEndpointImpl
creates a Binding
and Destination
to listen on.
Dependencies
CXF's dependencies
Quality
CXF's Software Quality approach is detailed here.
References