Intercepting web service calls with a custom Metro Tube

In this blog, I am going to show you how to write a custom tube for intercepting web service messages and how to configure Metro to route all the web service traffic through this custom tube.

As you may know, the upcoming Metro v2.0 release will quietly introduce a new “Declarative tubeline assembler” feature that lets advanced Metro users to write their own Tubes and specify custom tubelines for web services in their applications. The reason why we are not advertising this feature is that we feel there’s still some important work to be done in terms of consolidating all existing Metro configuration files and stabilizing some internal APIs enough to expose them publicly. So, before I continue with the blog that is clearly targeted at developers who like to live on the edge of new technologies, I need to clearly state the following “Safe Harbor” Statement:

Metro APIs discussed in this blog post are internal to Metro, evolving and may change in the future Metro releases without a prior warning. Use it at your own risk.

Good, so now that you have been properly warned (and scared), we can finally continue. As I have said, Metro 2.0 has this notion of declarative approach to assembling the web service processing tubeline, which you can imagine as a set of low-lever (and thus powerful) filters that are applied to each SOAP request/response that flows to/from a web service endpoint or its client. The standard set of tubes that comes with Metro takes care for all run-time processing including security, reliability, transactions, message validation, XML-Java and Java-XML transformations, business logic invocation etc.

The concept of tubes has been there in Metro for a quite a long time now. And, as I mentioned before, the concept is VERY powerful – many times more powerful than the standard JAX-WS handlers. Leveraging this concept at a user level was however difficult as there was no easy way how to insert a custom tube into a web service endpoint tubeline and thus effectively override the default Metro tubeline. The declarative tubeline assembler has changed that. It is now possible to write your own tube factories and use them in customizing the processing tubeline on the endpoint level basis. Let’s have a look at one of the potential use cases.

In this use case, I am about to develop an interceptor capable of intercepting all calls to all web service endpoints deployed on the application server instance regardless of the application in which they are deployed. I am using GlassFish v2 with latest build of Metro v2.0 installed on it.

I will start with a simple “echo” test service application. By invoking this service I will be able to verify that my interceptor kicks in and really works. The code of the service is pretty simple and straightforward:

public class Echo {
     * Web service operation
    @WebMethod(operationName = "echoMessage")
    public String echoMessage(@WebParam(name = "message")
    final String message) {
        return "Echoed: " + message;

Once the service is deployed I can create the client application to invoke it. I am using NetBeans web project and for simplicity I embed the service invocation code directly to the index.jsp page. The code looks like this:

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
        <h1>Hello World!</h1>
        <%-- start web service invocation --%><hr/>
                try {
                    com.sun.metro.samples.tubeinterceptor.testservice.EchoService service = new com.sun.metro.samples.tubeinterceptor.testservice.EchoService();
                    com.sun.metro.samples.tubeinterceptor.testservice.Echo port = service.getEchoPort();
                    String result = port.echoMessage("Hello");
                    out.println("Result = " + result);
                } catch (Exception ex) {
                    out.println("Exception = " + ex.toString());
        <%-- end web service invocation --%><hr/>

When I select “Run” from the client’s project context menu, the project gets deployed and the index page gets displayed with the result of invocation:

Echo Test Service Client Invocation

So far, it is a simple WS sample, nothing new, nothing special. Let’s start with the interesting stuff then – I am going to create a new project containing my interceptor.

To intercept the Metro messages, first I need to create a custom implementation of the JAX-WS Tube interface. I will extend my implementation from AbstractFilterTubeImpl class. This class provides some common logic and provides default implementations for methods in the Tube interface. In my implementation I am just adding some logging messages that inform that the interceptor has been invoked. Here’s the code:

final class InterceptorTube extends AbstractFilterTubeImpl {
    private static final Logger logger = Logger.getLogger(InterceptorTube.class.getName());
    static enum Side {
    private final Side side;
    private InterceptorTube(InterceptorTube original, TubeCloner cloner) {
        super(original, cloner);
        this.side = original.side;
    public InterceptorTube copy(TubeCloner cloner) {
        return new InterceptorTube(this, cloner);
    InterceptorTube(Tube tube, Side side) {
        this.side = side;
    public NextAction processRequest(Packet request) {
        // TODO: place your request processing code here"Message request intercepted on %s side", side));
        return super.processRequest(request);
    public NextAction processResponse(Packet response) {
        // TODO: place your response processing code here"Message response intercepted on %s side", side));
        return super.processResponse(response);
    public NextAction processException(Throwable throwable) {
        // TODO: place your error processing code here"Message processing exception intercepted on %s side", side));
        return super.processException(throwable);
    public void preDestroy() {
        try {
            // TODO: place your resource releasing code here
        } finally {

Of course, your interceptor can do something really fancy with the intercepted messages; it can filter or modify the payload, process message headers, add new message headers, change or fork processing flow, your imagination is the only limit.

The next step is to implement a new TubeFactory class that will be used by Metro run-time code to instantiate my InterceptorTube during the tubeline creation. Again, here’s the code:

public class InterceptorTubeFactory implements TubeFactory {
    private static final Logger logger = Logger.getLogger(InterceptorTubeFactory.class.getName());
    public Tube createTube(ClientTubelineAssemblyContext context) throws WebServiceException {"Creating client-side interceptor tube");
        return new InterceptorTube(context.getTubelineHead(), InterceptorTube.Side.Client);
    public Tube createTube(ServerTubelineAssemblyContext context) throws WebServiceException {"Creating server-side interceptor tube");
        return new InterceptorTube(context.getTubelineHead(), InterceptorTube.Side.Endpoint);

Normally, you would probably want to implement some logic that would decide whether or not the tube should be created, based on a presence of a particular WebServiceFeature or any other information from the tubeline assembly context, but in this scenario it is sufficient to just create a new interceptor tube whenever the factory method is invoked.

Now having both – tube and its factory – in place, I need to wire them with the Metro run-time. First of all, I have to create a custom metro.xmlconfig file that includes my interceptor tube factory and place it under META-INF directory inside my interceptor library jar. The easiest way to do that is to copy and modify the default Metro config file (metro-default.xml) which resides in webservices-rt.jar. Here’s the resulting config file:

<metro  xmlns:xsi=''
    <tubelines default="#intercepted-tubeline">
        <tubeline name="intercepted-tubeline">
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="com.sun.xml.wss.provider.wsit.SecurityTubeFactory" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="com.sun.xml.wss.provider.wsit.SecurityTubeFactory" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />
                <tube-factory className="" />

As you can see, I have inserted a reference to my custom tube factory into the chain of default Metro tube factories. The place of insertion depends on what you want to achieve and at what stage of processing you want to intercept. My factory is inserted very close to transport which lets my interceptor tube to see message in the same form as they are transferred over the wire. And now to the last piece. I want to make sure that Metro loads my config file for all web services in all applications deployed in the application server. Since Metro uses a classloader to load config files, I need to find a proper location where to put my library and perhaps modify the application server’s classpath. As one may expect, the proper location for my library in GlassFish v2 is in ${AS_HOME}/lib, where the run-time Metro jar resides. Also I need to make sure that my jar gets loaded before Metro jars, so I need to update GlassFish classpath settings and add my library to the classpath prefix:

GlassFish v2 Admin Console Classpath Settings

And now I just properly configure the logging levels to be able to see the relevant logging messages in the server log… -> INFO
javax.enterprise.resource.webservices.assembler -> FINER
javax.enterprise.resource.webservices -> INFO

…and restart the server. When I run the client again, in the server log I can see the following log messages which verify that my interceptor has been invoked for both, client and endpoint side in both request and response directions:

deployed with moduleid = TestServiceApp
Metro monitoring rootname successfully set to: com.sun.metro:pp=/,type=WSEndpoint,name=/TestServiceApp-EchoService-EchoPort
Default metro-default.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/webservices-rt.jar!/META-INF/metro-default.xml'
Application metro.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/InterceptorLib.jar!/META-INF/metro.xml'
Creating server-side interceptor tube
deployed with moduleid = TestClientApp
Trying to load 'metro-default.xml' via parent resouce loader '$1@13ffd111'
Default metro-default.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/webservices-rt.jar!/META-INF/metro-default.xml'
Trying to load 'metro.xml' via parent resouce loader '$1@13ffd111'
Application metro.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/InterceptorLib.jar!/META-INF/metro.xml'
Creating client-side interceptor tube
Message request intercepted on Client side
Message request intercepted on Endpoint side
Message response intercepted on Endpoint side
Message response intercepted on Client side

And that’s it. I have successfully created and configured a custom interceptor tube that intercepts all web service message processing on the server. The sample ZIP containing all three NetBeans projects is available here.

17 thoughts on “Intercepting web service calls with a custom Metro Tube

  1. Roman

    Hello. Thanks for a great post.

    I have a problem with metro 2.0 on Glassfish 2.1 (I’ve installed it manually). My tube factory cannot be loaded. JAXWS tube line assembler is created before the app is deployed. And tube factory has another classloader (metro classes are loaded by the parent one). So I’m getting ‘Unable to load tube factory class ‘ message within the runtime exception.

    Don’t you know how it can be fixed?

  2. Roman

    Oh, I’ve just read that you had placed your tube in AS_HOME/lib directory…

    But wan’t to notice that we have to do it because factory classes cannot be loaded. Configuration file is read perfectly (It’s done by the special loader class MetroConfigLoader). And only different classloaders make us to place jars to AS_HOME/lib. Hope this will be fixed…

  3. Marek Potociar Post author

    I did place my tube into $AS_HOME/lib so that it is available to ALL web services deployed on the GF instance. Normally, it should be sufficient to bundle your metro.xml config file and your tube factory class along with your web services within your application’s WAR

  4. xiong wei

    hi, Marek
    I have interest in metro tube, but I do not know where to get tutorial for tube. could u help provide some resource location? thanks

  5. Marek Potociar Post author

    Since Tube is not a public API, it is not documented for external users. But there are numerous blogs on that topic from my colleagues. Try googling for “tubes jaxws” and you should find some useful links.

  6. Adair

    Hi Marek,
    I’m trying to modify the payload in processRequest, so Message message = request.getMessage(); and
    SOAPMessage msg = message.readAsSOAPMessage(); but an error at the end of the method java.lang.NullPointerException at
    How modify the payload in processRequest?

  7. Marek Potociar Post author

    Hi Adair,
    I suggest to send this question to the mailing list. It would be very useful if you could also provide a reproducible test case for the issue that you see. Also, please don’t forget to specify Metro version and GlassFish/Tomcat version you use to run the code.


  8. Pingback: Custom tubes sample for Metro « Marek Potociar's Blog

  9. Amy

    is a customized tube also working with JBoss? I need to manipulate/ log my incoming/ outgoing SOAP requests (services are also implemented with Metro) without doing any changes on the Webservice code.
    I am doing my bachelor project at the moment and i am a newbie to all these technologies. Can you point me to any basic description how Metro Tubes/ Pipelining works?

    Best regards

  10. Marek Potociar Post author

    Hi Amy,
    the customization of the tubeline should work on JBoss too, provided you are using Metro with JBoss. Still, I would suggest you to send an email to in case you experience any problems with that. Metro has evolved since I wrote this blog entry, so the Metro team may be able to provide you with some fresh information and hints how to do things.


  11. Amy

    Hi Marek,
    thx for your reply! And thanks for your advice, I will contact the Metro team. Your blog helped me to understand much more of my issue!

  12. Justin Lindh

    Hi Marek,
    I know this post is a little old by now, but hopefully you’ll see it anyway. I’m trying to route SOAP calls in Metro based on the message’s “PayloadNamespaceURI”, and thought that tubes may be the answer. Essentially, I want multiple services running on the endpoint (different versions), and have the message routed to the correct service based on that namespace.

    The Packet “endpoint” property is interesting, and I’ve assigned the correct service to it in a tube interceptor… but that didn’t work. I’ve also tried “setEndPointAddressString” on the packet, but that doesn’t seem to work, either.

    Is there a way to change the routing on a message in the tube? Or maybe I’m going about this entirely the wrong way. I’d like to avoid using an ESB to deal with this, but maybe that’s not possible.


  13. Henock

    I was hoping to intercept secured web services is it possible to do that i have tried and it just throws exceptions thanks in advance

Leave a Reply

Your email address will not be published. Required fields are marked *