October 20, 2011

How to enable an interceptor without configuration in CXF

Imagine you develop an interceptor within your company which is for general use and not for a specific business project. There are different ways to configure an interceptor as described in more detail here. In summary:
  • by programming
  • configure the interceptor on the bus, client or endpoint level
  • configure a feature which registers interceptor(s)
  • configure a policy which registers interceptors
There might be use cases where you want to enable an interceptor just because the interceptor is available in the classpath. This means, you developed an interceptor, built a Jar and published to the maven repository. Another project should only add a dependency to your jar in their POM to enable the interceptor automatically. Done.

This blog will explain how this works and an example can be downloaded here.

CXF 2.4 introduced a new feature called bus extension which are loaded during the initialization phase of a bus automatically. Thus, your interceptor registration code must get notified whenever a new client or server is created to be able to add the interceptor. CXF provides three LifecycleListners:
This example shows the usage of the Server- and ClientLifecycleListner. A lifecycle listener is registered by the corresponding lifecycle manager which can be resolved using the CXF bus as illustrated in the following code snippet:

public class DemoListener implements ClientLifeCycleListener, ServerLifeCycleListener {

 public DemoListener(Bus bus) {
    ServerLifeCycleManager slm = bus.getExtension(ServerLifeCycleManager.class);
    slm.registerListener(this);

This class implements the lifecycle listener methods and registers itself to the lifecycle manager. The interceptor could be registered as illustrated in the following code snippet:

public void startServer(Server server) {
  System.out.println("--------- startServer");
  server.getEndpoint().getInInterceptors().add(new DemoInterceptor());
  server.getEndpoint().getOutInterceptors().add(new DemoInterceptor());
}

public void clientCreated(Client client) {
  System.out.println("--------- clientCreated");
  client.getOutInterceptors().add(new DemoInterceptor());
  client.getInInterceptors().add(new DemoInterceptor());
}

So far so good, but there must be a way to trigger the instantiation of the class DemoListener. One option is to use Spring and make this class a spring managed component or you register this bean as a CXF bus extension.

I'll explain how to register this bean as a bus extension thus it gets instantiated when the bus is created which means that the lifecycle listeners are registered in the startup phase too.
Your Jar file must contain a file called bus-extensions.xml at the following location in the Jar:
META-INF/cxf/bus-extensions.txt

The content of this text file is very simple. You just list the classname:
org.talend.ps.examples.busextension.DemoListener::false

The last parameter is false which tells the bus whether the instantiation of the bean should be defered or not. If deferred is true, (default) the bus doesn't create the beans during startup. However, if something specifically asks for one of those beans, it will be created and loaded.

If the class provides a constructor with the Bus argument, CXF will pass the bus during initialization.

Where else is this feature used? In CXF itself.

This was a very simple example how to instantiate beans during bus startup without the requirement for Spring. A lot of the advanced and WS-* features in CXF 2.3 and earlier required that you explicitly imported resources like:

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-jms.xml" />

This was required to import the spring configuration files which contained bean definitions which usually implemented lifecycle listeners or support for policy. This is not required any more in CXF 2.4 and above. You only have to import the following resource now:

<import resource="classpath:META-INF/cxf/cxf.xml" />

With CXF 2.4, these features are not plugged into the bus using a spring mechanism but instead the bus extension mechnism as illustrated in the simple example above.

If you want to see more advanced usage of bus extension have a look to the sources of some of these CXF modules:

An interesting interceptor example can be found in the Talend Service Factory.

3 comments:

  1. Hi Oliver,

    I 've found your article very interesting.
    I 've even implemented it in a Karaf runtime in order to get the client IP. Do you know if it is possible to put this IP in the message et get it in a Talend job ? By the way... I have designed a Talend Web service using the tESBProviderRequest and tESBProviderResponse components.

    Cheers.

    ReplyDelete
  2. Hi Oliver,

    I 've found your article very interesting.
    I 've even implemented it in a Karaf runtime in order to get the client IP. Do you know if it is possible to put this IP in the message et get it in a Talend job ? By the way... I have designed a Talend Web service using the tESBProviderRequest and tESBProviderResponse components.

    Cheers.

    ReplyDelete
  3. Hi

    Could you post this request at the talend forum here:
    http://www.talendforum.org/

    I haven't played much with tESBProviderRequest and tESBProviderResponse .

    Thanks
    Oli

    ReplyDelete