Interceptors has been edited by willem jiang (Nov 20, 2007).

Change summary:

Using the cxf:bus tag to configure the interceptor

(View changes)

Content:

Interceptors and Phases

Interceptors are the fundamental processing unit inside CXF. When a service is invoked, an InterceptorChain is created and invoked. Each interceptor gets a chance to do what they want with the message. This can include reading it, transforming it, processing headers, validating the message, etc.

Interceptors are relevant for both CXF clients and CXF servers. When a CXF client invokes a CXF server, there is an outgoing interceptor chain for the client and an incoming chain for the server. When the server sends the response back to the client, there is an outgoing chain for the server and an incoming one for the client.

Some examples of interceptors inside CXF include:

  • SoapActionInterceptor - Processes the SOAPAction header and selects an operation if its set.
  • StaxInInterceptor - Creates a Stax XMLStreamReader from the transport input stream.
  • Attachment(In/Out)Interceptor - Turns a multipart/related message into a series of attachments.

InterceptorChains are divided up into Phases. Each phase contains many interceptors. On the incoming chains, you'll have the following phases:

Phase Functions
RECEIVE Transport level processing
(PRE/USER/POST)_STREAM Stream level processing/transformations
READ This is where header reading typically occurs.
(PRE/USER/POST)_PROTOCOL Protocol processing, such as JAX-WS SOAP handlers
UNMARSHAL Unmarshalling of the request
(PRE/USER/POST)_LOGICAL Processing of the umarshalled request
PRE_INVOKE Pre invocation actions
INVOKE Invocation of the service
POST_INVOKE Invocation of the outgoing chain if there is one

On the outgoing chain there are the following phases:

Phase Functions
SETUP Any set up for the following phases
(PRE/USER/POST)_LOGICAL Processing of objects about to marshalled
PREPARE_SEND Opening of the connection
PRE_STREAM  
PRE_PROTOCOL Misc protocol actions.
WRITE Writing of the protocol message, such as the SOAP Envelope.
MARSHAL Marshalling of the objects
(USER/POST)_PROTOCOL Processing of the protocol message.
(USER/POST)_STREAM Processing of the byte level message
SEND Final sending of message and closing of transport stream

InterceptorProviders

Several different components inside CXF may provide interceptors to an InterceptorChain. These implement the InterceptorProvider interface:

public interface InterceptorProvider {

    List<Interceptor> getInInterceptors();

    List<Interceptor> getOutInterceptors();

    List<Interceptor> getOutFaultInterceptors();

    List<Interceptor> getInFaultInterceptors();
}

To get an interceptor added to the interceptor chain, you'll want to add it to one of the Interceptor Providers.

MyInterceptor interceptor = new MyInterceptor();
provider.getInInterceptors().add(interceptor);

Some InterceptorProviders inside CXF are:

  • Client
  • Endpoint
  • Service
  • Bus
  • Binding

Writing and configuring an Interceptor

CXF distribution is shipped with a demo called configuration_interceptor, this demo shows you how to develope an user interceptor and add the interceptor into the interceptor chain through configuration.

Writing an Interceptor

Writing an interceptor is relatively simple. Your interceptor needs to extend from either the AbstractPhaseInterceptor or some of its sub-classes such as AbstractSoapInterceptor. Extending from AbstractPhaseInterceptor allows your interceptor to access the Message class. For example, AttachmentInInterceptor is used in CXF to turn a multipart/related message into a series of attachments. It looks like below:

import java.io.IOException;

import org.apache.cxf.attachment.AttachmentDeserializer;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

public class AttachmentInInterceptor extends AbstractPhaseInterceptor<Message> {
    public AttachmentInInterceptor() {
        super(Phase.RECEIVE);
    }

    public void handleMessage(Message message) {
        String contentType = (String) message.get(Message.CONTENT_TYPE);
        if (contentType != null && contentType.toLowerCase().indexOf("multipart/related") != -1) {
            AttachmentDeserializer ad = new AttachmentDeserializer(message);
            try {
                ad.initializeAttachments();
            } catch (IOException e) {
                throw new Fault(e);
            }
        }
    }

    public void handleFault(Message messageParam) {
    }
}

Extending from sub-classes of AbstractPhaseInterceptor allows your interceptor to access more specific information than Message class. One of sub-classes of AbstractPhaseInterceptor is AbstractSoapInterceptor, extending from AbstractSoapInterceptor makes your interceptor being able to access the SoapMessage class which contains SOAP headers and version etc. For example, SoapActionInInterceptor is used in CXF to parse SOAP action, a simplified version looks like below:

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.cxf.binding.soap.Soap11;
import org.apache.cxf.binding.soap.Soap12;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.model.SoapOperationInfo;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.OperationInfo;

public class SoapActionInInterceptor extends AbstractSoapInterceptor {

    public SoapActionInInterceptor() {
        super(Phase.READ);
        addAfter(ReadHeadersInterceptor.class.getName());
        addAfter(EndpointSelectionInterceptor.class.getName());
    }

    public void handleMessage(SoapMessage message) throws Fault {
        if (message.getVersion() instanceof Soap11) {
            Map<String, List<String>> headers = CastUtils.cast((Map)message.get(Message.PROTOCOL_HEADERS));
            if (headers != null) {
                List<String> sa = headers.get("SOAPAction");
                if (sa != null && sa.size() > 0) {
                    String action = sa.get(0);
                    if (action.startsWith("\"")) {
                        action = action.substring(1, action.length() - 1);
                    }
                    getAndSetOperation(message, action);
                }
            }
        } else if (message.getVersion() instanceof Soap12) {
          ...........
        }
    }

    private void getAndSetOperation(SoapMessage message, String action) {
        if ("".equals(action)) {
            return;
        }

        Exchange ex = message.getExchange();
        Endpoint ep = ex.get(Endpoint.class);

        BindingOperationInfo bindingOp = null;

        Collection<BindingOperationInfo> bops = ep.getBinding().getBindingInfo().getOperations();
        for (BindingOperationInfo boi : bops) {
            SoapOperationInfo soi = (SoapOperationInfo) boi.getExtensor(SoapOperationInfo.class);
            if (soi != null && soi.getAction().equals(action)) {
                if (bindingOp != null) {
                    //more than one op with the same action, will need to parse normally
                    return;
                }
                bindingOp = boi;
            }
        }
        if (bindingOp != null) {
            ex.put(BindingOperationInfo.class, bindingOp);
            ex.put(OperationInfo.class, bindingOp.getOperationInfo());
        }
    }

}

You may also want to specify what phase your interceptor is in. To do this, you can simply set the phase:

public class MyInterceptor extends AbstractSoapInterceptor {
  public MyInterceptor() {
    super(Phase.USER_PROTOCOL);
  }
  ...
}

You can also express that you would like your interceptor to run before/after certain other interceptors in the same phase:

public class MyInterceptor extends AbstractSoapInterceptor {
  public MyInterceptor() {
    super(Phase.USER_PROTOCOL);
    getAfter().add(SomeOtherInterceptor.class.getName());
  }
  ...
}

You can add your interceptors into the interceptor chain either programmatically or through configuration.

Adding interceptors programmatically

To add this to your server, you'll want to get access to the Server object (see here for more info):

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.frontend.ServerFactoryBean;
...

MyInterceptor myInterceptor = new MyInterceptor();

Server server = serverFactoryBean.create();
server.getEndpoint().getInInterceptor().add(myInterceptor);

On the Client side the process is very similar:

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
...

FooService client = ... ; // created from ClientProxyFactoryBean or generated JAX-WS client
MyInterceptor myInterceptor = new MyInterceptor();

Client cxfClient = ClientProxy.getClient(client);
cxfClient.getInInterceptor().add(myInterceptor);

Adding interceptors through configuration

//TODO add the configuration file information

Adding MyInterceptor to the bus:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cxf="http://cxf.apache.org/core"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">

    <bean id="MyInterceptor" class="demo.interceptor.MyInterceptor"/>

    <!-- We are adding the interceptors to the bus as we will have only one endpoint/service/bus. -->

    <cxf:bus>
        <cxf:inInterceptors>
            <list>
                <ref bean="MyInterceptor"/>
            </list>
        </cxf:inInterceptors>
        <cxf:outInterceptors>
            <list>
                <ref bean="MyInterceptor"/>
            </list>
        </cxf:outInterceptors>
    </cxf:bus>
</beans>

Adding MyInterceptor to your client:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:http="http://cxf.apache.org/transports/http/configuration"
       xsi:schemaLocation="http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <http:conduit name="{http://apache.org/hello_world_soap_http}SoapPort9001.http-conduit">
      <http:client DecoupledEndpoint="http://localhost:9990/decoupled_endpoint"/>
    </http:conduit>

    <bean id="MyInterceptor" class="demo.interceptor.MyInterceptor"/>

    <!-- We are adding the interceptors to the bus as we will have only one endpoint/service/bus. -->

    <bean id="cxf" class="org.apache.cxf.bus.CXFBusImpl">
        <property name="inInterceptors">
            <list>
                <ref bean="MyInterceptor"/>
            </list>
        </property>
        <property name="outInterceptors">
            <list>
                <ref bean="MyInterceptor"/>
            </list>
        </property>
    </bean>
</beans>

Powered by Atlassian Confluence (Version: 2.2.9 Build:#527 Sep 07, 2006) - Bug/feature request

Unsubscribe or edit your notifications preferences