下面接着分析Context容器,该接口最重要的方法是addWrapper()方法与creatWrapper()方法,添加具体的子容器,这里是Wrapper容器实例
这里要分析的是一个简单的Context容器,它针对特定的客户端请求,通过映射器找到要处理该特定请求的子容器实例(Wrapper)
具体流程是,Context容器首先调用额外的阀,最后调用基础阀(这里是SimpleContextValve),在基础阀里面通过映射器找到要 处理该请求的子容器Wrapper实例,然后再调用子容器Wrapper实例的各个阀(本示例的Wrapper只有基础阀)(类似于composte模式 迭代)
下面是SimpleContext类的实现,SimpleContext实现org.apache.catalina.Context接口和org.apache.catalina.Pipeline接口
public class SimpleContext implements Context, Pipeline { public SimpleContext() { pipeline.setBasic(new SimpleContextValve()); } // 子容器名称与子容器实例映射 protected HashMap children = new HashMap(); protected Loader loader = null; protected SimplePipeline pipeline = new SimplePipeline(this); // servlet模式与子容器名称映射 protected HashMap servletMappings = new HashMap(); // 映射器 protected Mapper mapper = null; // 映射器协议与映射器实例映射 protected HashMap mappers = new HashMap(); private Container parent = null; /** * 添加servlet模式与子容器名称(wrapper)到HashMap servletMappings容器 * @param pattern * @param name */ public void addServletMapping(String pattern, String name) { synchronized (servletMappings) { servletMappings.put(pattern, name); } } /** * 根据servlet模式找到对应的子容器名称(wrapper)(供映射器调用) * @param pattern * @return */ public String findServletMapping(String pattern) { synchronized (servletMappings) { return ((String) servletMappings.get(pattern)); } } /** * 获取加载器 * @return */ public Loader getLoader() { if (loader != null) return (loader); if (parent != null) return (parent.getLoader()); return (null); } /** * 设置加载器 * @param loader */ public void setLoader(Loader loader) { this.loader = loader; } /** * 添加子容器实例(wrapper)名称与子容器实例(wrapper)到HashMap children容器 * @param child */ public void addChild(Container child) { child.setParent((Container) this); children.put(child.getName(), child); } /** * 根据名称查找子容器实例wrapper(供映射器调用) * @param name * @return */ public Container findChild(String name) { if (name == null) return (null); synchronized (children) { // Required by post-start changes return ((Container) children.get(name)); } } /** * 查找子容器数组 * @return */ public Container[] findChildren() { synchronized (children) { Container results[] = new Container[children.size()]; return ((Container[]) children.values().toArray(results)); } } /** * 添加映射器实例 * @param mapper */ public void addMapper(Mapper mapper) { // this method is adopted from addMapper in ContainerBase // the first mapper added becomes the default mapper mapper.setContainer((Container) this); // May throw IAE this.mapper = mapper; synchronized (mappers) { if (mappers.get(mapper.getProtocol()) != null) throw new IllegalArgumentException("addMapper: Protocol '" + mapper.getProtocol() + "' is not unique"); mapper.setContainer((Container) this); // May throw IAE mappers.put(mapper.getProtocol(), mapper); if (mappers.size() == 1) this.mapper = mapper; else this.mapper = null; } } /** * 根据协议查找映射器实例 * @param protocol * @return */ public Mapper findMapper(String protocol) { // the default mapper will always be returned, if any, // regardless the value of protocol if (mapper != null) return (mapper); else synchronized (mappers) { return ((Mapper) mappers.get(protocol)); } } public Mapper[] findMappers() { return null; } /** * 根据请求找到子容器实例wrapper (供基础阀调用该方法) * @param request * @param update * @return */ public Container map(Request request, boolean update) { // this method is taken from the map method in // org.apache.cataline.core.ContainerBase // the findMapper method always returns the default mapper, if any, // regardless the // request's protocol Mapper mapper = findMapper(request.getRequest().getProtocol()); if (mapper == null) return (null); // Use this Mapper to perform this mapping // 具体过程 (回调该对象下面方法) // 根据request找到处理该请求的子容器wrapper名称 调用方法 String findServletMapping(String pattern) // 根据上面的子容器wrapper名称找到子容器wrapper 调用方法 Container findChild(String name) return (mapper.map(request, update)); } public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); } // method implementations of Pipeline public Valve getBasic() { return pipeline.getBasic(); } public void setBasic(Valve valve) { pipeline.setBasic(valve); } public synchronized void addValve(Valve valve) { pipeline.addValve(valve); } public Valve[] getValves() { return pipeline.getValves(); } public void removeValve(Valve valve) { pipeline.removeValve(valve); } }
下面我们来分析映射器SimpleContextMapper的实现
public class SimpleContextMapper implements Mapper { /** * The Container with which this Mapper is associated. */ private SimpleContext context = null; public Container getContainer() { return (context); } public void setContainer(Container container) { if (!(container instanceof SimpleContext)) throw new IllegalArgumentException ("Illegal type of container"); context = (SimpleContext) container; } public String getProtocol() { return null; } public void setProtocol(String protocol) { } /** * Return the child Container that should be used to process this Request, * based upon its characteristics. If no such child Container can be * identified, return <code>null</code> instead. * * @param request Request being processed * @param update Update the Request to reflect the mapping selection? * * @exception IllegalArgumentException if the relative portion of the * path cannot be URL decoded */ public Container map(Request request, boolean update) { // Identify the context-relative URI to be mapped String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath(); String requestURI = ((HttpRequest) request).getDecodedRequestURI(); String relativeURI = requestURI.substring(contextPath.length()); // Apply the standard request URI mapping rules from the specification Wrapper wrapper = null; String servletPath = relativeURI; String pathInfo = null; String name = context.findServletMapping(relativeURI); if (name != null) wrapper = (Wrapper) context.findChild(name); return (wrapper); } }
映射器SimpleContextMapper最重要的方法是Container map(Request request, boolean update)
即根据客户端请求找到对应的子容器实例wrapper,里面关键代码是回调context容器实例的方法(持有对SimpleContext实例的引用)
接下里分析基础阀的关键代码(管道持有对基础阀的引用)
public class SimpleContextValve implements Valve, Contained { protected Container container; 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 } // Disallow any direct access to resources under WEB-INF or META-INF HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); String contextPath = hreq.getContextPath(); String requestURI = ((HttpRequest) request).getDecodedRequestURI(); String relativeURI = requestURI.substring(contextPath.length()).toUpperCase(); Context context = (Context) getContainer(); // Select the Wrapper to be used for this Request Wrapper wrapper = null; try { wrapper = (Wrapper) context.map(request, true); } catch (IllegalArgumentException e) { badRequest(requestURI, (HttpServletResponse) response.getResponse()); return; } if (wrapper == null) { notFound(requestURI, (HttpServletResponse) response.getResponse()); return; } // Ask this Wrapper to process this Request response.setContext(context); wrapper.invoke(request, response); } public Container getContainer() { return container; } public void setContainer(Container container) { this.container = container; } private void badRequest(String requestURI, HttpServletResponse response) { try { response.sendError(HttpServletResponse.SC_BAD_REQUEST, requestURI); } catch (IllegalStateException e) { ; } catch (IOException e) { ; } } private void notFound(String requestURI, HttpServletResponse response) { try { response.sendError(HttpServletResponse.SC_NOT_FOUND, requestURI); } catch (IllegalStateException e) { ; } catch (IOException e) { ; } } }
基础阀持有对Context容器实例(SimpleContext)的引用,在它的关键方法void invoke(Request request, Response response, ValveContext valveContext)里面,先调用Context容器实例的Container map(Request request, boolean update)方法获取子容器实例wrapper,最后调用子容器实例wrapper的invoke(Request request, Response response)方法
至于管道类SimplePipeline与上文相同,此处不再描述
---------------------------------------------------------------------------
本系列How Tomcat Works系本人原创
转载请注明出处 博客园 刺猬的温驯
本人邮箱: chenying998179#163.com (#改为@)