在tomcat中责任链模式还有一个非常典型的应用:http消息在container之间的传递。

具体过程在文章Tomcat源码分析HTTP消息处理(从connectorservlet中已经详细分析过,本文着重分析容器间管道和阀门的实现。

Tomcat源码分析HTTP消息处理(从connectorservlet中提到,消息传递过程中每个容器内都会有这样一样代码:

xxxx. getPipeline().getFirst().invoke(request, response);

这里就是通过管道对阀的调用。下面分析阀是怎么加入到管道并连接起来的。这里举例分析Host容器,其他容器过程与Host相同。

1.      为容器加入管道

在容器的基类ContainerBase 中用成员变量

protected Pipeline pipeline =
        CatalinaFactory.getFactory().createPipeline(this);

public class CatalinaFactory {
    
    private static CatalinaFactory factory = new CatalinaFactory();
    
    public static CatalinaFactory getFactory() {
        return factory;
    }
    
    private CatalinaFactory() {
        // Hide the default constructor
    }
    
    public String getDefaultPipelineClassName() {
        return StandardPipeline.class.getName();
    }

    public Pipeline createPipeline(Container container) {
        Pipeline pipeline = new StandardPipeline();
        pipeline.setContainer(container);
        return pipeline;
    }
}

由此,每个容器都有一个StandardPipeline类型的pipeline

2. pipeline设置基本的阀门

public StandardHost() {

        	super();
        	pipeline.setBasic(new StandardHostValve());

 }

basicValveinvoke中会调用下一个容器。

3. 增加阀门

getPipeline().addValve(valve);

 

StandardPipeline 中会保存 first basic 两个阀门,新加阀门会增加到 basic 之前。 valve 本身是一个单向链表,会保存 next

        // Add this Valve to the set associated with this Pipeline
        if (first == null) {
        	first = valve;
        	valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
				if (current.getNext() == basic) {
					current.setNext(valve);
					valve.setNext(basic);
					break;
				}
				current = current.getNext();
			}
        }


通过2,3两步,pipeline中增加了一个从firstbasicvalve列表。

4. 阀门的调用

valve的基类ValveBase中有虚方法

publicabstractvoid invoke(Requestrequest, Response response)

       throws IOException,ServletException;

                每个子类都会实现这个方法,这里面完成需要的valve逻辑。这里作为basicValve在invoke中会调用下一个容器。其他容器都必须调用getNext().invoke(request,response);除非想特意让pipeline中断,转到你需要的容器中。

5.      加入第三方valve

可以通过继承ValveBase或者org.apache.catalina.valves中的类实现自己的valve。这里要特别注意,如果不想改变消息的传递方向的话,在实现invoke方法时一定要加入getNext().invoke(request,response)。

还需要在server.xml中给容器增加valve配置,如:


<Engine defaultHost="localhost" name="Catalina">

    <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
    ………
    <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" 
            xmlNamespaceAware="false" xmlValidation="false">

        <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
            directory="logs"  prefix="localhost_access_log." suffix=".txt"
            pattern="common" resolveHosts="false"/>           
    …………
    </Host>
</Engine>


以上,pipeline-valve的原理和增加方法就分析完了。通过pipeline-valve,tomcat的使用者不仅可以完成消息过滤,还可以改变消息的流向。在不改变tomcat原来逻辑的情况下实现扩展tomcat。

 



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐