CXF 之 Interceptor

在Web Service中,客户端和服务端通过交换信息来互相通信。信息在客户端组装,在服务端解组。在Web Service术语中,组装表示将JAVA对象转换为XML文件,这些XML文档将被传输到网络中;反而言之,解组就是将XML文档转换为JAVA对象。

当客户端向服务端发送请求,请求中的数据将被组装并传输到服务器。服务器获取该数据,解组,最后调用服务方法。当服务器发送响应给客户端时,将重复该过程。组装和解组是客户端和服务端提供的核心功能。CXF通过Interceptor来提供这些功能。

Interceptor通过监听传输过来的信息来提供核心功能。这些功能包括:组装、解组、操纵消息头、执行认证检查、验证消息数据等。CXF提供内置的Interceptor来实现这些功能。用户也可以自定义Interceptor。Interceptor以phases组织起来,以链的形式调用。

Interceptor是CXF架构中一个重要的功能。你可以在不对核心模块进行修改的情况下,动态添加很多功能。这对于CXF这个以处理消息为中心的服务框架来说是非常有用的,CXF通过在Interceptor中对消息进行特殊处理,实现了很多重要功能模块,例如:日志记录,Soap消息处理,消息的压缩处理。

CXF已经实现了很多种拦截器,很多已经在发布、访问Web 服务时已经默认添加到拦截器链。一般情况下, 我们自己的拦截器只要继承AbstractPhaseInterceptor<T extends org.apache.cxf.message.Message>类即可,这个类可以指定继承它的拦截器在什么阶段被启用,阶段属性可以通过org.apache.cxf.phase.Phase 中的常量指定值。


理解interceptor phase 和chain

Phase可以被当作分类框,将类似功能的Interceptor组织起来。

有两种Interceptor链,inbound链和outbound链。两种都有一系列的Phase。例如,在inbound链中有UNMARSHAL Phase,用来解组信息数据,并将其转化为JAVA对象。

对于每个请求来讲,在服务端创建inbound Interceptor;对于每个响应来讲,将创建outbound Interceptor。

消息传输到链中,按特定的顺序在Phase中的Interceptor执行。

在信息传输到服务端之前,inbound Interceptor操作信息。

在信息传输到客户端之前,outbound Interceptor操作信息。

如果出现错误,Interceptor链释放自己,不去调用应用。

interceptor API概览

PhaseInterceptor继承自Interceptor接口。AbstractPhaseInterceptor实现了PhaseInterceptor。

1.Interceptor接口:

如果要自定义Interceptor,就必须直接或间接实现Interceptor接口。Interceptor接口定义了两个方法。handleMessage和handleFault。

package org.apache.cxf.interceptor;

public interface Interceptor<T extends Message> {

   void handleMessage(T message) throws Fault;

   void handleFault(T message);

}

handleMessage方法需要org.apache.cxf.message.Message对象。这是执行信息的核心方法。为了自定义Interceptor就必须实习该方法,并提供执行信息的逻辑。

handleFault方法同样需要org.apache.cxf.message.Message对象。在执行Interceptor过程中出现错误时将会调用该方法。

2.PhaseInterceptor接口:

大多数核心Interceptor都是实现继承自Interceptor接口的PhaseInterceptor。

PhaseInterceptor定义了4个方法。

getAfter将返回一个Set,包含了在这个Interceptor之前执行Interceptor的IDs。

getBefore将返回一个Set,包含了在这个Interceptor之后执行Interceptor的IDs。

getId返回ID。

getPhase返回这个Interceptor所在的phase。

为了自定义Interceptor,开发者必须继承AbstractPhaseInterceptor抽象类。

3.AbstractPhaseInterceptor抽象类

AbstractPhaseInterceptor定义了构造器,可以为自定义的Interceptor指定特定的phase。当你指定了phase,自定义的Interceptor将会安排在链中Phase。AbstractPhaseInterceptor提供了空实现的handleFault。开发者可以覆盖这个方法。开发者必须实现handleMessage方法。

查看AbstractPhaseInterceptor抽象类,它已实现PhaseInterceptor接口的方法。Phase的顺序由PhaseInterceptorChain类决定。

开发自定义的interceptor

为了演示interceptor的功能,假定一个用例:只有经过认证的用户才能调用Web Service,用户认证需要获取SOAP头的信息。

为了实现这些需求,创建两个interceptor,一个客户端,一个服务端。客户端interceptor负责拦截即将发出的SOAP信息,并在SOAP头中添加用户验证。服务端interceptor负责拦截收到的SOAP信息,从SOAP信息获取用户认证信息并验证该用户。如果用户验证失败,将抛出异常,在这种情况下阻止Web Service的运行。

开发自定义的interceptor分为如下几个步骤:

1.开发服务端的interceptor。

2.在Web Service服务类添加服务端interceptor。

3.开发客户端interceptor。

4.在客户端添加客户端interceptor。

5.开发发布Web Service的服务器。

开发服务端interceptor

OrderProcessUserCredentialInterceptor继承自AbstractSoapInterceptor,AbstractSoapInterceptor继承自AbstractPhaseInterceptor。AbstractSoapInterceptor提供了获取SOAP头和版本的信息。

OrderProcessUserCredentialInterceptor默认构造函数中,指定了Phase.PRE_INVOKE,代表着该interceptor将先于Web Service服务类执行。

public OrderProcessUserCredentialInterceptor() {

        super(Phase.PRE_INVOKE);

    }

handleMessage方法中,SoapMessage提供了获取SOAP头的信息。使用message.getHeader获取SOAP头中<OrderCredentials>元素

Header header = message.getHeader(qnameCredentials);

<soap:Header>

      <OrderCredentials>

         <username>John</username>

         <password>password</password>

      </OrderCredentials>

</soap:Header>

在OrderCredentials节点下,可以获得用户名和密码节点。

       Element elementOrderCredential = (Element) header.getObject();

      Node nodeUser = elementOrderCredential.getFirstChild();

      Node nodePassword = elementOrderCredential.getLastChild();

package demo.order.server;

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class OrderProcessUserCredentialInterceptor extends AbstractSoapInterceptor {

    private String userName;
    private String password;

    public OrderProcessUserCredentialInterceptor() {
        super(Phase.PRE_INVOKE);
    }

    public void handleMessage(SoapMessage message) throws Fault {

        System.out.println("OrderProcessUserCredentialInterceptor handleMessage invoked");
        QName qnameCredentials = new QName("OrderCredentials");

        // Get header based on QNAME
        if (message.hasHeader(qnameCredentials)) {
            Header header = message.getHeader(qnameCredentials);

            Element elementOrderCredential = (Element) header.getObject();
            Node nodeUser = elementOrderCredential.getFirstChild();
            Node nodePassword = elementOrderCredential.getLastChild();

            if (nodeUser != null) {
                userName = nodeUser.getTextContent();
            }
            if (nodePassword != null) {
                password = nodePassword.getTextContent();
            }
        }

        System.out.println("userName reterived from SOAP Header is " + userName);
        System.out.println("password reterived from SOAP Header is " + password);

        // Perform dummy validation for John
        if ("John".equalsIgnoreCase(userName) && "password".equalsIgnoreCase(password)) {
            System.out.println("Authentication successful for John");
        } else {
            throw new RuntimeException("Invalid user or password");
        }
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


Web Service业务类添加interceptor

Web Service业务类可以添加配置文件或者定义annotations添加interceptor。

添加InInterceptors注释定义了一个Inbound interceptor,将在Web Service执行之前调用。

@org.apache.cxf.interceptor.InInterceptors (interceptors = {"demo.

order.server.OrderProcessUserCredentialInterceptor" })

package demo.order;

import javax.jws.WebService;

@org.apache.cxf.interceptor.InInterceptors(interceptors = {"demo.order.server.OrderProcessUserCredentialInterceptor"})
@WebService
public class OrderProcessImpl implements OrderProcess {

    public String processOrder(Order order) {
        System.out.println("Processing order...");
        String orderID = validate(order);
        return orderID;
    }

    /**
     * Validates the order and returns the order ID
*
     */
    private String validate(Order order) {
        String custID = order.getCustomerID();
        String itemID = order.getItemID();
        int qty = order.getQty();
        double price = order.getPrice();

        if (custID != null && itemID != null && qty > 0 && price > 0.0) {
            return "ORD1234";
        }

        return null;
    }
}


开发客户端interceptor

同样的OrderProcessClientHandler继承自AbstractSoapInterceptor。

OrderProcessClientHandler默认构造器,指定了super(Phase.WRITE),还addAfter方法。addAfter指定了OrderProcessClientHandler将在CXF内置SoapPreProtocolOutInterceptor之后添加。OrderProcessClientHandler将在Phase.WRITE并在SoapPreProtocolOutInterceptor之后运行。SoapPreProtocolOutInterceptor负责建立SOAP版本和头,因此任何SOAP头元素的操作都必须在SoapPreProtocolOutInterceptor之后执行。

public OrderProcessClientHandler() {

      super(Phase.WRITE);

      addAfter(SoapPreProtocolOutInterceptor.class.getName());

}


构造函数new Header(qnameCredentials, elementCredentials);创建了Header对象,并设置了OrderCredentials元素。最后message.getHeaders().add(header)将SOAP头添加到信息中。

package demo.order.client;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class OrderProcessClientHandler extends AbstractSoapInterceptor {

    public String userName;
    public String password;

    public OrderProcessClientHandler() {
        super(Phase.WRITE);
        addAfter(SoapPreProtocolOutInterceptor.class.getName());
    }

    public void handleMessage(SoapMessage message) throws Fault {

        System.out.println("OrderProcessClientHandler handleMessage invoked");

        DocumentBuilder builder = null;
        try {
            builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        Document doc = builder.newDocument();
        Element elementCredentials = doc.createElement("OrderCredentials");
        Element elementUser = doc.createElement("username");
        elementUser.setTextContent(getUserName());
        Element elementPassword = doc.createElement("password");
        elementPassword.setTextContent(getPassword());
        elementCredentials.appendChild(elementUser);
        elementCredentials.appendChild(elementPassword);

        // Create Header object
        QName qnameCredentials = new QName("OrderCredentials");
        Header header = new Header(qnameCredentials, elementCredentials);
        message.getHeaders().add(header);
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}


客户端添加interceptor

OrderProcessClientHandler设置了username和password。通过ClientProxy.getClient(client);获取Client对象,并通过cxfClient.getOutInterceptors().add(clientInterceptor)将clientInterceptor作为outbound interceptor。

package demo.order.client;

import demo.order.OrderProcess;
import demo.order.Order;

import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.gzip.GZIPInInterceptor;
import org.apache.cxf.transport.http.gzip.GZIPOutInterceptor;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Client {

    public Client() {
    }

    public static void main(String args[]) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"client-beans.xml"});

        OrderProcess client = (OrderProcess) context.getBean("orderClient");
//        OrderProcess client = (OrderProcess) context.getBean("orderClient2");
        OrderProcessClientHandler clientInterceptor = new OrderProcessClientHandler();
        clientInterceptor.setUserName("John");
        clientInterceptor.setPassword("password");
        org.apache.cxf.endpoint.Client cxfClient = ClientProxy.getClient(client);
        cxfClient.getOutInterceptors().add(clientInterceptor);

        Order order = new Order();
        order.setCustomerID("C001");
        order.setItemID("I001");
        order.setQty(100);
        order.setPrice(200.00);

        String orderID = client.processOrder(order);
        String message = (orderID == null) ? "Order not approved" : "Order approved; order ID is " + orderID;
        System.out.println(message);
    }
}


开发发布Web Service的服务器

package demo.order;

import org.apache.cxf.feature.LoggingFeature;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.transport.http.gzip.GZIPFeature;
import org.apache.cxf.transport.http.gzip.GZIPInInterceptor;
import org.apache.cxf.transport.http.gzip.GZIPOutInterceptor;

public class OrderProcessServerStart {

    public static void main(String[] args) throws Exception {

        OrderProcess orderProcess = new OrderProcessImpl();
        LoggingFeature log = new LoggingFeature();
        GZIPFeature gzip = new GZIPFeature();
        gzip.setThreshold(1);
        JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();
        server.setServiceBean(orderProcess);
        server.setAddress("http://localhost:8080/OrderProcess");

        server.getFeatures().add(log);
        server.getFeatures().add(gzip);
        //server.getFeatures().add(log);
        //server.getInInterceptors().add(new LoggingInInterceptor());
        //server.getOutInterceptors().add(new LoggingOutInterceptor());


        server.create();
        System.out.println("Server ready....");



        Thread.sleep(5 * 60 * 1000);
        System.out.println("Server exiting");
        System.exit(0);
    }
}


附录:

InterceptorChains are divided up into Phases. The phase that each interceptor runs in is declared in the interceptor's constructor. Each phase may contain many interceptors. On the incoming chains, you'll have the following phases:





On the outgoing chain there are the following phases:




After the SEND phase, there are a bunch of "*_ENDING" phases that are symmetrical to the above phases to allow the interceptors to cleanup and close anything that they had opened or started in the above phases:







猜你喜欢

转载自maosheng.iteye.com/blog/2003933