JBossWS integration layer with Apache CXF
All JAX-WS functionalities provided by JBossWS on top of JBoss Application Server are currently served through a proper integration of the JBoss Web Services stack with most of the Apache CXF project modules.
Apache CXF is an open source services framework. It allows building and developing services using frontend programming APIs (including JAX-WS), with services speaking a variety of protocols such as SOAP and XML/HTTP over a variety of transports such as HTTP and JMS.
The integration layer (JBossWS-CXF in short hereafter) is mainly meant for:
-
allowing using standard webservices APIs (including JAX-WS) on JBoss Application Server; this is performed internally leveraging Apache CXF without requiring the user to deal with it;
-
allowing using Apache CXF advanced features (including WS-*) on top of JBoss Application server without requiring the user to deal with / setup / care about the required integration steps for running in such a container.
In order for achieving the goals above, the JBossWS-CXF integration supports the JBoss ws endpoint deployment mechanism and comes with many internal customizations on top of Apache CXF.
In the next sections a list of technical suggestions and notes on the integration is provided; please also refer to the Apache CXF official documentation for in-depth details on the CXF architecture.
Building WS applications the JBoss way
The Apache CXF client and endpoint configuration as explained in the Apache CXF official user guide is heavily based on Spring. Apache CXF basically parses Spring cxf.xml descriptors; those may contain any basic bean plus specific ws client and endpoint beans which CXF has custom parsers for. Apache CXF can be used to deploy webservice endpoints on any servlet container by including its libraries in the deployment; in such a scenario Spring basically serves as a convenient configuration option, given direct Apache CXF API usage won't be very handy. Similar reasoning applies on client side, where a Spring based descriptor offers a shortcut for setting up Apache CXF internals.
This said, nowadays almost any Apache CXF functionality can be configured and used through direct API usage, without Spring.
Portable applications
The JBoss Application Server is much more then a servlet container; it actually provides users with a fully compliant target platform for Java EE applications.
Generally speaking, users are encouraged to write portable applications by relying only on JAX-WS specification whenever possible. That would by the way ensure easy migrations to and from other compliant platforms. Being a Java EE container, JBoss Application Server already comes with a JAX-WS compliant implementation, which is basically Apache CXF plus the JBossWS-CXF integration layer. So users just need to write their JAX-WS application; no need for embedding any Apache CXF or any ws related dependency library in user deployments. Please refer to the JAX-WS User Guide section of the documentation for getting started.
WS-* usage (including WS-Security, WS-Addressing, WS-ReliableMessaging, ...) should also be configured in the most portable way; that is by relying on proper WS-Policy assertions on the endpoint WSDL contracts, so that client and endpoint configuration is basically a matter of setting few ws context properties. The WS-* related sections of this documentation cover all the details on configuring applications making use of WS-* through policies.
As a consequence of the reasoning above, the JBossWS-CXF integration is currently built directly on the Apache CXF API and aims at allowing users to configure webservice clients and endpoints without Spring descriptors.
The following two paragraphs provide few directions on how to deploy or use applications explicitly relying on Apache CXF, users should however prefer the portable application approach whenever possible.
Direct Apache CXF API usage
Whenever users can't really meet their application requirements with JAX-WS plus WS-Policy, it is of course still possible to rely on direct Apache CXF API usage (given that's included in the AS), loosing the Java EE portability of the application. That could be the case of a user needing specific Apache CXF functionalities, or having to consume WS-* enabled endpoints advertised through legacy wsdl contracts without WS-Policy assertions.
On server side, direct Apache CXF API usage might not be always possible or end up being not very easy. For this reason, the JBossWS integration comes with a convenient alternative through customization options in the jboss-webservices.xml descriptor described below on this page.
Spring descriptors usage
Finally, in some cases, users might still want to consume Spring descriptors (discouraged approach); that's possibly the case of applications developed on and being migrated from different environments. For such scenarios, the installation of Spring Framework libraries on application server is the suggested approach. That can be performed using the JBossWS-CXF installation script or by manually populating a org.springframework.spring JBoss AS module with the required Spring jars. For writing the module.xml descriptor for such a module please refer the relevant JBoss AS documentation on creating modules; in any case it would look similar to:
<module xmlns="urn:jboss:module:1.1" name="org.springframework.spring">
<resources>
<!-- List references to jar resources here -->
</resources>
<dependencies>
<module name="javax.api" />
<module name="javax.jms.api" />
<module name="javax.annotation.api" />
<module name="org.apache.commons.logging" />
<module name="org.jboss.vfs" />
</dependencies>
</module>
The other webservices modules on JBoss AS already have an optional dependency on org.springframework.spring module and will hence automatically consume it.
Once the Spring module is available on target application server, Spring based Apache CXF buses can be built up.
Client side
Whenever Spring is available in the current thread classloader (possibly as a consequence of having set a dependency to the above mentioned org.springframework.spring module) and the classloader can successfully locate a valid Spring descriptor resource, a Spring based Bus will be created if required. So user can either:
-
programmatically use a SpringBusFactory (or the JBossWSBusFactory if the JBossWS additions are available) to load a Spring Bus from a given cxf.xml descriptor; that can include any CXF customization or client bean;
-
build a JAX-WS client and let the JAX-WS Provider implementation internally build a Spring based Bus using the available cxf.xml resource retrieved from the current classloader (usually found in META-INF/cxf.xml).
Consider having a look at this page for directions on setting module dependencies, especially if willing to create a ws client within a Spring Bus and running in-container.
Finally please be sure to check the section below on Bus usage any time you're building a Bus on client side.
Server side
It is possible to customize the JBossWS integration with Apache CXF by incorporating a CXF configuration file into the endpoint deployment archive. The convention is the following:
-
the descriptor file name must be jbossws-cxf.xml
-
for POJO deployments it is located in WEB-INF directory
-
for EJB3 deployments it is located in META-INF directory
The jbossws-cxf.xml is parsed similarly to a common cxf.xml in order for building up a Bus for the WS deployment; the endpoint beans included in the deployment are to be specified using the <jaxws:endpoint> tag the same they would be specified in a cxf.xml descriptor (a example from the testsuite can be seen here). The application server HTTP engine will be serving the endpoints.
If there is no <jaxws:endpoint> defined in jbossws-cxf.xml, the endpoint classes mentioned in WEB-INF/web.xml will be automatically transformed to <jaxws:endpoint> entries in the Spring configuration and loaded by JBossWS-CXF. This allows using the jbossws-cxf.xml to customize the bus without having to manually duplicate the endpoint information in the descriptor. The following is an example of configuring an endpoint through web.xml with Aegis databinding setup from jbossws-cxf.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<servlet>
<servlet-name>AegisGroupQueryService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.cxf.aegis.AegisGroupQueryImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AegisGroupQueryService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
<beans xmlns='http://www.springframework.org/schema/beans'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:beans='http://www.springframework.org/schema/beans'
xmlns:jaxws='http://cxf.apache.org/jaxws'
xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.w3.org/2006/07/ws-policy http://www.w3.org/2006/07/ws-policy.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd'>
<bean id="aegisBean" class="org.apache.cxf.aegis.databinding.AegisDatabinding" scope="prototype" />
<bean name="{http://aegis.cxf.jaxws.ws.test.jboss.org/}AegisGroupQueryImplPort.jaxws-endpoint" abstract="true">
<property name="dataBinding" ref="aegisBean" />
</bean>
</beans>
The jbossws-cxf.xml approach can be used for the very few scenarios Apache CXF can be configured for only using Spring descriptor, e.g. for some advanced WS-RM customizations.
Bus usage
Creating a Bus instance
Most of the Apache CXF features are configurable using the org.apache.cxf.Bus class. While for basic JAX-WS usage the user might never need to explicitly deal with Bus, using Apache CXF specific features generally requires getting a handle to a org.apache.cxf.Bus instance. This can happen on client side as well as in a ws endpoint or handler business code.
New Bus instances are produced by the currently configured org.apache.cxf.BusFactory implementation the following way:
Bus bus = BusFactory.newInstance().createBus();
The algorithm for selecting the actual implementation of BusFactory to be used leverages the Service API, basically looking for optional configurations in META-INF/services/... location using the current thread context classloader. JBossWS-CXF integration comes with its own implementation of BusFactory, org.jboss.wsf.stack.cxf.client.configuration.JBossWSBusFactory, that allows for automatic detection of Spring availability as well as seamless setup of JBossWS customizations on top of Apache CXF. So, assuming the JBossWS-CXF libraries are available in the current thread context classloader, the JBossWSBusFactory is automatically retrieved by the BusFactory.newInstance() call above.
JBossWS users willing to explicitely use functionalities of org.apache.cxf.bus.spring.SpringBusFactory or org.apache.cxf.bus.CXFBusFactory, get the same API with JBossWS additions through JBossWSBusFactory:
String myConfigFile = ...
Bus bus = new JBossWSBusFactory().createBus(myConfigFile);
Map<Class, Object> myExtensions = new HashMap<Class, Object>();
myExtensions.put(...);
Bus bus = new JBossWSBusFactory().createBus(myExtensions);
Using existing Bus instances
Apache CXF keeps reference to a global default Bus instance as well as to a thread default bus for each thread. That is performed through static members in org.apache.cxf.BusFactory, which also comes with the following methods in the public API:
public static synchronized Bus getDefaultBus()
public static synchronized Bus getDefaultBus(boolean createIfNeeded)
public static synchronized void setDefaultBus(Bus bus)
public static Bus getThreadDefaultBus()
public static Bus getThreadDefaultBus(boolean createIfNeeded)
public static void setThreadDefaultBus(Bus bus)
Please note that the default behaviour of getDefaultBus() / getDefaultBus(true) / getThreadDefaultBus() / getThreadDefaultBus(true) is to create a new Bus instance if that's not set yet. Moreover getThreadDefaultBus() and getThreadDefaultBus(true) first fallback to retrieving the configured global default bus before actually trying creating a new instance (and the created new instance is set as global default bus if that was not set there yet).
The drawback of this mechanism (which is basically fine in JSE environment) is that when running in a JBoss AS container you need to be careful in order not to (mis)use a bus over multiple applications (assuming the Apache CXF classes are loaded by the same classloader, which is currently the case with JBoss AS6, JBoss AS7 and WildFly 8).
Here is a list of general suggestions to avoid problems when running in-container:
-
forget about the global default bus; you don't need that, so don't do getDefaultBus() / getDefaultBus(true) / setDefaultBus() in your code;
-
avoid getThreadDefaultBus() / getThreadDefaultBus(true) unless you already know for sure the default bus is already set;
-
keep in mind thread pooling whenever you customize a thread default bus instance (for instance adding bus scope interceptors, ...), as that thread and bus might be later reused; so either shutdown the bus when you're done or explicitly remove it from the BusFactory thread association.
Finally, remember that each time you explictly create a new Bus instance (factory.createBus()) that is set as thread default bus and global default bus if those are not set yet. The JAXWS Provider implementation also creates Bus instances internally, in particular the JBossWS version of JAXWS Provider makes sure the default bus is never internally used and instead a new Bus is created if required (more details on this in the next paragraph).
Bus selection strategies for JAXWS clients
JAXWS clients require an Apache CXF Bus to be available; the client is registered within the Bus and the Bus affects the client behavior (e.g. through the configured CXF interceptors). The way a bus is internally selected for serving a given JAXWS client is very important, especially for in-container clients; for this reason, JBossWS users can choose the preferred Bus selection strategy. The strategy is enforced in the javax.xml.ws.spi.Provider implementation from the JBossWS integration, being that called whenever a JAXWS Service (client) is requested.
Thread bus strategy (THREAD_BUS)
Each time the vanilla JAXWS api is used to create a Bus, the JBossWS-CXF integration will automatically make sure a Bus is currently associated to the current thread in the BusFactory. If that's not the case, a new Bus is created and linked to the current thread (to prevent the user from relying on the default Bus). The Apache CXF engine will then create the client using the current thread Bus.
This is the default strategy, and the most straightforward one in Java SE environments; it lets users automatically reuse a previously created Bus instance and allows using customized Bus that can possibly be created and associated to the thread before building up a JAXWS client.
The drawback of the strategy is that the link between the Bus instance and the thread needs to be eventually cleaned up (when not needed anymore). This is really evident in a Java EE environment (hence when running in-container), as threads from pools (e.g. serving web requests) are re-used.
When relying on this strategy, the safest approach to be sure of cleaning up the link is to surround the JAXWS client with a try/finally block as below:
try {
Service service = Service.create(wsdlURL, serviceQName);
MyEndpoint port = service.getPort(MyEndpoint.class);
//...
} finally {
BusFactory.setThreadDefaultBus(null);
// OR (if you don't need the bus and the client anymore)
Bus bus = BusFactory.getThreadDefaultBus(false);
bus.shutdown(true);
}
New bus strategy (NEW_BUS)
Another strategy is to have the JAXWS Provider from the JBossWS integration create a new Bus each time a JAXWS client is built. The main benefit of this approach is that a fresh bus won't rely on any formerly cached information (e.g. cached WSDL / schemas) which might have changed after the previous client creation. The main drawback is of course worse performance as the Bus creation takes time.
If there's a bus already associated to the current thread before the JAXWS client creation, that is automatically restored when returning control to the user; in other words, the newly created bus will be used only for the created JAXWS client but won't stay associated to the current thread at the end of the process. Similarly, if the thread was not associated to any bus before the client creation, no bus will be associated to the thread at the end of the client creation.
Thread context classloader bus strategy (TCCL_BUS)
The last strategy is to have the bus created for serving the client be associated to the current thread context classloader (TCCL). That basically means the same Bus instance is shared by JAXWS clients running when the same TCCL is set. This is particularly interesting as each web application deployment usually has its own context classloader, so this strategy is possibly a way to keep the number of created Bus instances bound to the application number in a JBoss AS container.
If there's a bus already associated to the current thread before the JAXWS client creation, that is automatically restored when returning control to the user; in other words, the bus corresponding to the current thread context classloader will be used only for the created JAXWS client but won't stay associated to the current thread at the end of the process. If the thread was not associated to any bus before the client creation, a new bus will be created (and later user for any other client built with this strategy and the same TCCL in place); no bus will be associated to the thread at the end of the client creation.
Strategy configuration
Users can request a given Bus selection strategy to be used for the client being built by specifying one of the following JBossWS features (which extend javax.xml.ws.WebServiceFeature):
Feature
|
Strategy
|
org.jboss.wsf.stack.cxf.client.UseThreadBusFeature
|
THREAD_BUS
|
org.jboss.wsf.stack.cxf.client.UseNewBusFeature
|
NEW_BUS
|
org.jboss.wsf.stack.cxf.client.UseTCCLBusFeature
|
TCCL_BUS
|
The feature is specified as follows:
Service service = Service.create(wsdlURL, serviceQName, new UseThreadBusFeature());
If no feature is explicitly specified, the system default strategy is used, which can be modified through the org.jboss.ws.cxf.jaxws-client.bus.strategy system property when starting the JVM. The valid values for the property are THREAD_BUS, NEW_BUS and TCCL_BUS. The default is THREAD_BUS.
Server Side Integration Customization
The JBossWS-CXF server side integration takes care of internally creating proper Apache CXF structures (including a Bus instance, of course) for the provided ws deployment. Should the deployment include multiple endpoints, those would all live within the same Apache CXF Bus, which would of course be completely separated by the other deployments' bus instances.
While JBossWS sets sensible defaults for most of the Apache CXF configuration options on server side, users might want to fine tune the Bus instance that's created for their deployment; a jboss-webservices.xml descriptor can be used for deployment level customizations.
Deployment descriptor properties
The jboss-webservices.xml descriptor can be used to provide property values.
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2">
...
<property>
<name>...</name>
<value>...</value>
</property>
...
</webservices>
JBossWS-CXF integration comes with a set of allowed property names to control Apache CXF internals. The main advantage of the property based approach is that it does not require Spring libraries.
WorkQueue configuration
Apache CXF uses WorkQueue instances for dealing with some operations (e.g. @Oneway requests processing). A WorkQueueManager is installed in the Bus as an extension and allows for adding / removing queues as well as controlling the existing ones.
On server side, queues can be provided through Spring based Bus declaration or by using the cxf.queue.<queue-name>.* properties in jboss-webservices.xml (e.g. cxf.queue.default.maxQueueSize for controlling the max queue size of the default workqueue). At deployment time, the JBossWS integration can add new instances of AutomaticWorkQueueImpl to the currently configured WorkQueueManager; the properties below are used to fill in parameter into the AutomaticWorkQueueImpl constructor:
Property
|
Default value
|
cxf.queue.<queue-name>.maxQueueSize
|
256
|
cxf.queue.<queue-name>.initialThreads
|
0
|
cxf.queue.<queue-name>.highWaterMark
|
25
|
cxf.queue.<queue-name>.lowWaterMark
|
5
|
cxf.queue.<queue-name>.dequeueTimeout
|
120000
|
Policy alternative selector
The Apache CXF policy engine supports different strategies to deal with policy alternatives. JBossWS-CXF integration currently defaults to the MaximalAlternativeSelector, but still allows for setting different selector implementation using the cxf.policy.alternativeSelector property in jboss-webservices.xml.
MBean management
Apache CXF allows managing its MBean objects that are installed into the JBoss AS MBean server. The feature is enabled on a deployment basis through the cxf.management.enabled property in jboss-webservices.xml. The cxf.management.installResponseTimeInterceptors property can also be used to control installation of CXF response time interceptors, which are added by default when enabling MBean management, but might not be desired in some cases. Here is an example:
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2">
<property>
<name>cxf.management.enabled</name>
<value>true</value>
</property>
<property>
<name>cxf.management.installResponseTimeInterceptors</name>
<value>false</value>
</property>
</webservices>
Schema validation
Schema validation of exchanged messages can also be enabled in jboss-webservices.xml. Further details available here.
Discovery enablement
WS-Discovery support can be turned on in jboss-webservices for the current deployment. Further details available here.