10.容器

 

容器是一个处理用户servlet请求并返回对象给web用户的模块。
org.apache.catalina.Container接口定义了容器的形式,有四种容器:Engine(引擎), Host(主机), Context(上下文), 和 Wrapper(包装器)。
容器接口
一个容器必须实现org.apache.catalina.Container接口
传递一个Container实例给Connector对象的setContainer方法,然后Connector对象就可以使用container的invoke方法
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
对于Catalina的容器首先需要注意的是它一共有四种不同的容器:
Engine:表示整个Catalina的servlet引擎
Host:表示一个拥有数个上下文的虚拟主机
Context:表示一个Web应用,一个context包含一个或多个wrapper
Wrapper:表示一个独立的servlet
每一个概念之上是用org.apache.catalina包来表示的。
Engine、Host、Context和Wrapper接口都实现了Container即可。
它们的标准实现是StandardEngine, StandardHost, StandardContext, and StandardWrapper,它们都是org.apache.catalina.core包的一部分。

 



 


一个Catalina功能部署不一定需要所有的四种类型的容器。例如本章的第一个应用程序仅仅包括一个wrapper,而第二个应用程序是一个包含Context和wrapper的容器模块。
一个容器可以有一个或多个低层次上的子容器。例如,一个Context有一个或多个wrapper,而wrapper作为容器层次中的最底层,不能包含子容器。
Container接口被设计成Tomcat管理员可以通过server.xml文件配置来决定其工作方式的模式

Pipelining Tasks(流水线任务)
一个pipeline包含了改容器要唤醒的所有任务。每一个阀门表示了一个特定的任务。一个容器的流水线有一个基本的阀门,但是你可以添加任意你想要添加的阀门。
阀门的数目定义为添加的阀门的个数(不包括基本阀门)。有趣的是,阀门可以通过编辑Tomcat的配置文件server.xml来动态的添加。

理解了servlet过滤器,那么流水线和它的阀门的工作方式不难想象。
一个流水线就像一个过滤链,每一个阀门像一个过滤器。跟过滤器一样,一个阀门可以操作传递给它的request和response方法。
让一个阀门完成了处理,则进一步处理流水线中的下一个阀门,基本阀门总是在最后才被调用。

一个容器可以有一个流水线。当容器的invoke方法被调用的时候,容器将会处理流水线中的阀门,并一个接一个的处理,直到所有的阀门都被处理完毕
流水线的invoke方法的伪代码如下所示
// invoke each valve added to the pipeline
for (int n=0; n<valves.length; n++) {
 valve[n].invoke( ... );
}
// then, invoke the basic valve
basicValve.invoke( ... );

 

Tomcat的设计者选择了一种通过org.apache.catalina.ValveContext定义的方式来处理,这里介绍它如何工作的:
容器的invoke方法在被connector调用的时候所作的工作不难进行编码。容器调用的是流水线的invoke方法。
流水线接口的invoke方法前面跟容器接口的invoke方法签名相同
public void invoke(Request request, Response response) throws IOException, ServletException;
这里是Container接口中invoke方法在org.apache.catalina.core.ContainerBase的实现:
public void invoke(Request request, Response response)throws IOException, ServletException {
 pipeline.invoke(request, response);
}
Pipeline是容器中Pipeline接口的一个实例。
流水线必须保证说要添加给它的阀门必须被调用一次,流水线通过创建一个ValveContext接口的实例来实现它。
ValveContext是流水线的的内部类,这样ValveContext就可以访问流水线中所有的成员。ValveContext中最重要的方法是invokeNext方法
public void invokeNext(Request request, Response response) throws IOException, ServletException
在创建一个ValveContext实例之后,流水线调用ValveContext的invokeNext方法。ValveContext会先唤醒流水线的第一个阀门,然后第一个阀门会在完成它的任务之前唤醒下一个阀门。
ValveContext将它自己传递给每一个阀门,那么该阀门就可以调用ValveContext的invokeNext方法。Valve接口的invoke签名如下:
public void invoke(Request request, Response response, ValveContext ValveContext) throws IOException, ServletException

public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException {
 // Pass the request and response on to the next valve in our pipeline
 valveContext.invokeNext(request, response);
 // now perform what this valve is supposed to do ...
}

org.apache.catalina.core.StandardPipeline类是容器流水线的实现
InvokeNext方法使用下标(subscript)和级别(stage)记住哪个阀门被唤醒。当第一次唤醒的时候,下标的值是0,级的值是1。
以你次,第一个阀门被唤醒,流水线的阀门获得ValveContext实例调用它的invokeNext方法。这时下标的值是1所以下一个阀门被唤醒,然后一步步的进行。

The Pipeline Interface流水线接口
我们提到的流水线的第一个方法是它的Pipeline接口的invoke方法,该方法会开始唤醒流水线的阀门。流水线接口允许你添加一个新的阀门或者删除一个阀门。
最后,可以使用setBasic方法来分配一个基本阀门给流水线,getBasic方法会得到基本阀门。最后被唤醒的基本阀门,负责处理request和回复response。
public interface Pipeline {
 public Valve getBasic();
 public void setBasic(Valve valve);
  public void addValve(Valve valve);
 public Valve[] getValves();
 public void invoke(Request request, Response response) throws IOException, ServletException;
 public void removeValve(Valve valve);
}

The Valve Interface阀门接口
阀门接口表示一个阀门,该组件负责处理请求。该接口有两个方法,invoke和getInfo方法。
Invoke方法如上所述,getInfo方法返回阀门的信息
public interface Valve {
 public String getInfo();
 public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException;
}

The ValveContext Interface阀门上下文接口
阀门上下文接口有两个方法,invokeNext方法如上所述,getInfo方法会返回阀门上下文的信息。ValveContext接口如下:
public interface ValveContext {
 public String getInfo();
 public void invokeNext(Request request, Response response)throws IOException, ServletException;
}


The Contained Interface Contained接口
一个阀门可以选择性的实现org.apache.catalina.Contained接口。该接口定义了其实现类跟一个容器相关联
public interface Contained {
 public Container getContainer();
 public void setContainer(Container container);
}

the Wrapper Interface Wrapper接口
org.apache.catalina.Wrapper接口表示了一个包装器。一个包装器是表示一个独立servlet定义的容器。
包装器继承了Container接口,并且添加了几个方法。包装器的实现类负责管理其下层servlet的生命中期,包括servlet的init,service,和destroy方法。
由于包装器是最底层的容器,所以不可以将子容器添加给它。如果addChild方法被调用的时候会产生IllegalArgumantException异常。
包装器接口中重要方法有allocate和load方法。allocate方法负责定位该包装器表示的servlet的实例
Load方法负责load和初始化servlet的实例

The Context Interface上下文(Context)接口
一个context在容器中表示一个web应用。一个context通常含有一个或多个包装器作为其子容器。
重要的方法包括addWrapper, createWrapper等方法

The Wrapper Application(包装器应用程序)
这个应用程序展示了如何写一个简单的容器模型
核心类是SimpleWrapper,它实现了Wrapper接口。SimpleWrapper类包括一个Pipeline和一个Loader类来加载一个servlet。
流水线包括一个基本阀门SimpleWrapperValve和两个另外的阀门ClientIPLoggerValve,HeaderLoggerValve)

 



 

public static void main(String[] args) {
		
		/**
		 * 初始化连接器
		 */
		HttpConnector connector = new HttpConnector();
		
		//创建一个包装器
		Wrapper wrapper = new SimpleWrapper();
		//告诉包装器要加载类的名字
		wrapper.setServletClass("ModernServlet");
		//创建了加载器
		Loader loader = new SimpleLoader(); 
		//两个阀门
		Valve valve1 = new HeaderLoggerValve(); 
		Valve valve2 = new ClientIPLoggerValve();
		//把加载器给包装器
		wrapper.setLoader(loader);
		//流水线添加俩个阀门
		((Pipeline) wrapper).addValve(valve1);
		((Pipeline) wrapper).addValve(valve2);
		//连接器设置容器
		//把包装器当做容器添加到连接器中,然后初始化并启动连接器
		connector.setContainer(wrapper);
		
		try{
			//连接器初始化,实现Lifecycle周期接口
			connector.initialize();
			//启动
			connector.start();
			System.in.read();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

 

SimpleWrapper

package com.tomcat.core;

import java.beans.PropertyChangeListener;
import java.io.IOException;

import javax.naming.directory.DirContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;

import org.apache.catalina.AccessLog;
import org.apache.catalina.Cluster;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerListener;
import org.apache.catalina.InstanceListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Manager;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Realm;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

/**
 * 代表一个容器
 * @author Administrator
 *
 */
public class SimpleWrapper implements Wrapper {
	
	/**
	 * loader变量用于加载一个servlet类
	 */
	private Loader loader; 
	/**
	 * Parent变量表示该包装器的父容器
	 */
	protected Container parent = null;
	
	/**
	 * getLoader方法用于返回一个Loader对象用于加载一个servlet类。
	 * 如果一个包装器跟一个加载器相关联,会返回该加载器。
	 * 否则返回其父容器的加载器,如果没有父容器,则返回null。
	 */
	public Loader getLoader() {
		if(loader!=null)
			return loader;
		if(parent!=null)
			return parent.getLoader();
		return null;
	}

	public void addInitParameter(String arg0, String arg1) {
		// TODO Auto-generated method stub

	}

	public void addInstanceListener(InstanceListener arg0) {
		// TODO Auto-generated method stub

	}

                .......

}

 

SimpleLoader

 

package com.tomcat.core;

import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;

import org.apache.catalina.Container;
import org.apache.catalina.Loader;

/**
 * 容器中加载servlet的任务被分配给了Loader实现
 * impleLoader就是一个Loader实现。
 * 它知道如何定位一个servlet,并且通过getClassLoader获得一个java.lang.ClassLoader实例用来查找servlet类位置
 * @author Administrator
 *
 */
public class SimpleLoader implements Loader {

	WEB_ROOT用来指明在哪里查找servlet类
	public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
	
	ClassLoader classLoader = null; 
	Container container = null;
	
	/**
	 * 初始化类加载器
	 */
	public SimpleLoader(){
		try {
			URL[] urls = new URL[1];
			URLStreamHandler streamHandler = null;
			File classPath = new File(WEB_ROOT);
			String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
			urls[0] = new URL(null, repository, streamHandler);
			classLoader = new URLClassLoader(urls);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void addPropertyChangeListener(PropertyChangeListener arg0) {
		// TODO Auto-generated method stub
		
	}

	......................


}

 

HeaderLoggerValve

 

 

package com.tomcat.core;

import java.io.IOException;

import javax.servlet.ServletException;

import org.apache.catalina.CometEvent;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

/**
 * 是一个阀门
 * 打印请求头部到控制台上
 * @author Administrator
 *
 */
public class HeaderLoggerValve implements Valve, Contained {

	public void backgroundProcess() {
		// TODO Auto-generated method stub

	}

	public void event(Request arg0, Response arg1, CometEvent arg2)
			throws IOException, ServletException {
		// TODO Auto-generated method stub

	}

	public String getInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	public Valve getNext() {
		// TODO Auto-generated method stub
		return null;
	}

	public void invoke(Request arg0, Response arg1) throws IOException,
			ServletException {
		// TODO Auto-generated method stub

	}

	...................

}

 

ClientIPLoggerValve

 

package com.tomcat.core;

import java.io.IOException;

import javax.servlet.ServletException;

import org.apache.catalina.CometEvent;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

/**
 * ClientIPLoggerValve是一个阀门,它打印出客户端的IP地址到控制台
 * @author Administrator
 *
 */
public class ClientIPLoggerValve implements Valve,Contained{

	protected Container container;
	
	................................

	

}

 

 

SimplePipeline

package com.tomcat.core;

import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;

/**
 * 流水线
 * @author Administrator
 *
 */
public class SimplePipeline implements Pipeline {

	public void addValve(Valve arg0) {
		// TODO Auto-generated method stub

	}

	public Valve getBasic() {
		// TODO Auto-generated method stub
		return null;
	}

	public Valve getFirst() {
		// TODO Auto-generated method stub
		return null;
	}

	public Valve[] getValves() {
		// TODO Auto-generated method stub
		return null;
	}

	public void removeValve(Valve arg0) {
		// TODO Auto-generated method stub

	}

	public void setBasic(Valve arg0) {
		// TODO Auto-generated method stub

	}

}

 

 

 

一个仅仅包括一个包装器的简单的web应用。该程序仅仅包括一个servlet
大多数的网络应用需要多个servlet。在这些应用中,需要一个跟包装器不同的容器:上下文。
如何使用一个包含两个包装器的上下文来包装两个servlet类。
当有多于一个得包装器的时候,需要一个map来处理这些子容器,对于特殊的请求可以使用特殊的子容器来处理。

在这个程序中,mapper是SimpleContextMapper类的一个实例,
它继承了Tomcat 4中的org.apache.catalina.Mapper接口。一个容器也可以有多个mapper来支持多协议。
例如容器可以用一个mapper来支持HTTP协议,而使用另一个mapper来支持HTTPS协议

public interface Mapper {
 public Container getContainer();
 public void setContainer(Container container);
 public String getProtocol(); public void setProtocol(String protocol);
 public Container map(Request request, boolean update);
}
getContainer返回该容器的mapper,
setContainer方法用于联系一个容器到mapper。
 getProtocol返回该mapper负责处理的协议,
setProtocol用于分配该容器要处理的协议。
map方法返回处理一个特殊请求的子容器。

 



 

 

 

SimpleContext表示一个上下文,它使用SimpleContextMapper作为它的mapper,SimpleContextValve作为它的基本阀门。
该上下文包括两个阀门ClientIPLoggerValve和HeaderLoggerValve。
用SimpleWrapper表示的两个包装器作为该上下文的子容器被添加。
包装器吧SimpleWrapperValve作为它的基本阀门,但是没有其它的阀门了。
该上下文应用程序使用同一个加载器、两个阀门。但是加载器和阀门时跟该上下文关联的,而不是跟包装器关联。
这样,两个加载器就可以都使用该加载器。该上下文被当做连接器的容器

基本流程如下
1. 一个容器有一个流水线,容器的invoke方法会调用流水线的invoke方法。
2. 流水线的invoke方法会调用添加到容器中的阀门的invoke方法,然后调用基本阀门的invoke方法。
3. 在一个包装器中,基本阀门负责加载相关的servlet类并对请求作出相应。
4. 在一个有子容器的上下文中,基本法门使用mapper来查找负责处理请求的子容器。如果一个子容器被找到,子容器的invoke方法会被调用,然后返回步骤1。

 

package com.tomcat.startup;

import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;

import com.tomcat.core.ClientIPLoggerValve;
import com.tomcat.core.HeaderLoggerValve;
import com.tomcat.core.HttpConnector;
import com.tomcat.core.Mapper;
import com.tomcat.core.SimpleContext;
import com.tomcat.core.SimpleContextMapper;
import com.tomcat.core.SimpleLoader;
import com.tomcat.core.SimpleWrapper;

public final class Bootstrap2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		HttpConnector connector = new HttpConnector();
	    Wrapper wrapper1 = new SimpleWrapper();
	    wrapper1.setName("Primitive");
	    wrapper1.setServletClass("PrimitiveServlet");
	    Wrapper wrapper2 = new SimpleWrapper();
	    wrapper2.setName("Modern");
	    wrapper2.setServletClass("ModernServlet");

	    Context context = new SimpleContext();
	    context.addChild(wrapper1);
	    context.addChild(wrapper2);

	    Valve valve1 = new HeaderLoggerValve();
	    Valve valve2 = new ClientIPLoggerValve();

	    ((Pipeline) context).addValve(valve1);
	    ((Pipeline) context).addValve(valve2);

	    Mapper mapper = new SimpleContextMapper();
	    mapper.setProtocol("http");
	    context.addMapper(mapper);
	    Loader loader = new SimpleLoader();
	    context.setLoader(loader);
	    // context.addServletMapping(pattern, name);
	    context.addServletMapping("/Primitive", "Primitive");
	    context.addServletMapping("/Modern", "Modern");
	    connector.setContainer(context);
	    try {
	      connector.initialize();
	      connector.start();

	      // make the application wait until we press a key.
	      System.in.read();
	    }
	    catch (Exception e) {
	      e.printStackTrace();
	    }
	  }
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值