How Tomcat Works学习笔记<十二>

来源:互联网 发布:光盘刻录大师刻录数据 编辑:程序博客网 时间:2024/04/27 13:41

  Host和Engine

         在Catalina中Engine代表Catalina实体,Host代表一个虚拟的主机,Engine包括多个Host,一个Host包括多个Context。

Host接口

         在Tomcat中Host用org.apache.catalina.Host接口表示:

         public interface Host extends Container {

public static final String ADD_ALIAS_EVENT = "addAlias";

        public static final String REMOVE_ALIAS_EVENT = "removeAlias";

        public String getAppBase();

        public void setAppBase(String appBase);

        public boolean getAutoDeploy();

        public void setAutoDeploy(boolean autoDeploy);

        public void addDefaultContext(DefaultContext defaultContext);

        public DefaultContext getDefaultContext();

        public String getName();

        public void setName(String name);

        public void importDefaultContext(Context context);

        public void addAlias(String alias);

        public String[] findAliases();

        public Context map(String uri);

        public void removeAlias(String alias);

}

其中最重要的是map方法,它会根据请求调用正确的Context来处理。

StandardHost

StandardHost是Host的标准实现,在其构造方法中也会为管道中添加StandardHostValue:

public StandardHost() {

        super();

        pipeline.setBasic(new StandardHostValve());

}

同时在启动的时候还会添加两个Value:

public synchronized void start() throws LifecycleException {

        // Set error report valve

        if ((errorReportValveClass != null)

            && (!errorReportValveClass.equals(""))) {

            try {

                Valve valve = (Valve) Class.forName(errorReportValveClass)

                    .newInstance();

                addValve(valve);

            } catch (Throwable t) {

                log(sm.getString

                    ("standardHost.invalidErrorReportValveClass",

                     errorReportValveClass));

            }

        }

        // Set dispatcher valve

        addValve(new ErrorDispatcherValve());

        super.start();

 }

当HTTP请求来的时候,Host的invoke方法会被调用,其中会调用到StandardHostValue的invoke方法,通过调用Host的map方法找到正确的Context来处理请求:

public Context map(String uri) {

        if (debug > 0)

            log("Mapping request URI '" + uri + "'");

        if (uri == null)

            return (null);

        // Match on the longest possible context path prefix

        if (debug > 1)

            log("  Trying the longest context path prefix");

        Context context = null;

        String mapuri = uri;

        while (true) {

            context = (Context) findChild(mapuri);

            if (context != null)

                break;

            int slash = mapuri.lastIndexOf('/');

            if (slash < 0)

                break;

            mapuri = mapuri.substring(0, slash);

        }

        // If no Context matches, select the default Context

        if (context == null) {

            if (debug > 1)

                log("  Trying the default context");

            context = (Context) findChild("");

        }

        // Complain if no Context has been selected

        if (context == null) {

            log(sm.getString("standardHost.mappingError", uri));

            return (null);

        }

        // Return the mapped Context (if any)

        if (debug > 0)

            log(" Mapped to context '" + context.getPath() + "'");

        return (context);

}

StandardHostMapper

         在StandardHost中有一个映射器类StandardHostMapper专门用来根据请求uri调用StandardHost的map方法找到对应的Context进行处理,其map方法实现如下:

         public Container map(Request request, boolean update) {

        // Has this request already been mapped?

        if (update && (request.getContext() != null))

            return (request.getContext());

        // Perform mapping on our request URI

        String uri = ((HttpRequest) request).getDecodedRequestURI();

        Context context = host.map(uri);

        // Update the request (if requested) and return the selected Context

        if (update) {

            request.setContext(context);

            if (context != null)

                ((HttpRequest) request).setContextPath(context.getPath());

            else

                ((HttpRequest) request).setContextPath(null);

        }

        return (context);

}

StandardHostValue

         StandardHostValue是StandardHost的基础Value,其中invoke方法实现如下:

         public void invoke(Request request, Response response,

                       ValveContext valveContext)

        throws IOException, ServletException {

        // Validate the request and response object types

        if (!(request.getRequest() instanceof HttpServletRequest) ||

            !(response.getResponse() instanceof HttpServletResponse)) {

            return;     // NOTE - Not much else we can do generically

        }

        // Select the Context to be used for this Request

        StandardHost host = (StandardHost) getContainer();

        Context context = (Context) host.map(request, true);

        if (context == null) {

            ((HttpServletResponse) response.getResponse()).sendError

                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

                 sm.getString("standardHost.noContext"));

            return;

        }

        // Bind the context CL to the current thread

        Thread.currentThread().setContextClassLoader

            (context.getLoader().getClassLoader());

        // Update the session last access time for our session (if any)

        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();

        String sessionId = hreq.getRequestedSessionId();

        if (sessionId != null) {

            Manager manager = context.getManager();

            if (manager != null) {

                Session session = manager.findSession(sessionId);

                if ((session != null) && session.isValid())

                    session.access();

            }

        }

        // Ask this Context to process this request

        context.invoke(request, response);

}

在这里,它首先获取匹配的Context,然后获得session并调用session的access方法更新session的操作时间,最后调用context的invoke方法。

为什么必须要Host

         每个Context都需要ContextConfig来进行配置,需要通过ApplicationContext的getResource方法来获取应用的部署文件“WEB-INF/web.xml”,在getResource中需要用到Host:

         public URL getResource(String path)

        throws MalformedURLException {

        DirContext resources = context.getResources();

        if (resources != null) {

            String fullPath = context.getName() + path;

            // this is the problem. Host must not be null

            String hostName = context.getParent().getName();

            try {

                resources.lookup(path);

                if( System.getSecurityManager() !=null ) {

                    try {

                        PrivilegedGetResource dp =

                            new PrivilegedGetResource

                                (hostName, fullPath, resources);

                        return (URL)AccessController.doPrivileged(dp);

                    } catch( PrivilegedActionException pe) {

                        throw pe.getException();

                    }

                } else {

                    return new URL

                        ("jndi", null, 0, getJNDIUri(hostName, fullPath),

                         new DirContextURLStreamHandler(resources));

                }

            } catch (Exception e) {

                //e.printStackTrace();

            }

        }

        return (null);

}

Engine接口

         Engine表示catalina的servlet引擎实体,如果你要用到多个Host就必须用到Engine,在Tomcat中引擎用org.apache.catalina.Engine接口来实现。通常情况下Tomcat部署的时候都会用到engine。

         public interface Engine extends Container {

        public String getDefaultHost();

        public void setDefaultHost(String defaultHost);

        public String getJvmRoute();

        public void setJvmRoute(String jvmRouteId);

        public Service getService();

        public void setService(Service service);

        public void addDefaultContext(DefaultContext defaultContext);

        public DefaultContext getDefaultContext();

        public void importDefaultContext(Context context);

}

从接口定义中我们可以看到你可以为Engine添加一个默认的Host和默认的Engine,同时可以为Engine分配一个service。

StandardEngine

         StandardEngine是Engine接口的标准实现,在构造的时候为管道中添加基础value:

         public StandardEngine() {

        super();

        pipeline.setBasic(new StandardEngineValve());

}

同时你可以为Engine添加Host:

public void addChild(Container child) {

        if (!(child instanceof Host))

            throw new IllegalArgumentException

                (sm.getString("standardEngine.notHost"));

        super.addChild(child);

}

同时Engine没有父容器:

public void setParent(Container container) {

        throw new IllegalArgumentException

            (sm.getString("standardEngine.notParent"));

}

StandardEngineValue

    StandardEngineValue是StandardEngine的基础value,它的invoke方法如下:

    public void invoke(Request request, Response response,

                       ValveContext valveContext)

        throws IOException, ServletException {

        // Validate the request and response object types

        if (!(request.getRequest() instanceof HttpServletRequest) ||

            !(response.getResponse() instanceof HttpServletResponse)) {

            return;     // NOTE - Not much else we can do generically

        }

        // Validate that any HTTP/1.1 request included a host header

        HttpServletRequest hrequest = (HttpServletRequest) request;

        if ("HTTP/1.1".equals(hrequest.getProtocol()) &&

            (hrequest.getServerName() == null)) {

            ((HttpServletResponse) response.getResponse()).sendError

                (HttpServletResponse.SC_BAD_REQUEST,

                 sm.getString("standardEngine.noHostHeader",

                              request.getRequest().getServerName()));

            return;

        }

        // Select the Host to be used for this Request

        StandardEngine engine = (StandardEngine) getContainer();

        Host host = (Host) engine.map(request, true);

        if (host == null) {

            ((HttpServletResponse) response.getResponse()).sendError

                (HttpServletResponse.SC_BAD_REQUEST,

                 sm.getString("standardEngine.noHost",

                              request.getRequest().getServerName()));

            return;

        }

        // Ask this Host to process this request

        host.invoke(request, response);

}

当验证了请求(request)和响应(response)的类型以后,调用Engine的map方法获取Host用来处理来自客户端的HTTP请求。

 

 

原创粉丝点击