/* * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package org.ireland.jnetty.webapp; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.EventListener; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import javax.annotation.PostConstruct; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterRegistration; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.ServletRequestListener; import javax.servlet.SessionCookieConfig; import javax.servlet.UnavailableException; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tomcat.InstanceManager; import org.ireland.jnetty.beans.BeanFactory; import org.ireland.jnetty.config.ConfigException; import org.ireland.jnetty.config.WebXmlLoader; import org.ireland.jnetty.dispatch.FilterChainInvocation; import org.ireland.jnetty.dispatch.HttpInvocation; import org.ireland.jnetty.dispatch.filter.FilterConfigImpl; import org.ireland.jnetty.dispatch.filter.FilterManager; import org.ireland.jnetty.dispatch.filter.FilterMapper; import org.ireland.jnetty.dispatch.filter.FilterMapping; import org.ireland.jnetty.dispatch.filterchain.ExceptionFilterChain; import org.ireland.jnetty.dispatch.filterchain.ServletRequestListenerFilterChain; import org.ireland.jnetty.dispatch.servlet.ServletConfigImpl; import org.ireland.jnetty.dispatch.servlet.ServletManager; import org.ireland.jnetty.dispatch.servlet.ServletMapper; import org.ireland.jnetty.dispatch.servlet.ServletMapping; import org.ireland.jnetty.jsp.JspServletComposite; import org.ireland.jnetty.loader.WebAppClassLoader; import org.ireland.jnetty.server.session.SessionManager; import org.ireland.jnetty.util.http.URIDecoder; import org.springframework.util.Assert; import com.caucho.util.LruCache; /** * Resin's webApp implementation. */ public class WebApp extends ServletContextImpl { private static final Log log = LogFactory.getLog(WebApp.class.getName()); private static final boolean debug = log.isDebugEnabled(); // The context path is the URL prefix for the web-app private String _contextPath = ""; // The environment class loader private ClassLoader _classLoader; private String _host; private String _hostName = ""; private String _serverName = ""; private int _serverPort; // The webbeans container private BeanFactory _beanFactory; private URIDecoder _uriDecoder; private String _servletVersion; // -----servlet-------------------------- // The servlet manager private ServletManager _servletManager; // The servlet mapper private ServletMapper _servletMapper; // -----servlet-------------------------- // -----filter-------------------------- // The filter manager private FilterManager _filterManager; // The dispatch filter mapper (DispatcherType#REQUEST) private FilterMapper _dispatchFilterMapper; // The forward filter mapper (DispatcherType#FORWARD) private FilterMapper _forwardFilterMapper; // The include filter mapper (DispatcherType#INCLUDE) private FilterMapper _includeFilterMapper; // The error filter mapper (DispatcherType#ERROR) private FilterMapper _errorFilterMapper; // -----filter-------------------------- // The FilterChain Cache // 用LRU算法Cache最近最常使用的url与FilterChain之间的映射关系() private LruCache<String, FilterChainInvocation> _dispatchFilterChainCache = new LruCache<String, FilterChainInvocation>(128); private LruCache<String, FilterChainInvocation> _forwardFilterChainCache = new LruCache<String, FilterChainInvocation>(128); private LruCache<String, FilterChainInvocation> _includeFilterChainCache = new LruCache<String, FilterChainInvocation>(32); private LruCache<String, FilterChainInvocation> _errorFilterChainCache = new LruCache<String, FilterChainInvocation>(32); // <rowContextURI,_requestDispatcherCache> private LruCache<String, RequestDispatcherImpl> _requestDispatcherCache; // True for SSL secure. private boolean _isSecure; // Error pages. private ErrorPageManager _errorPageManager; private String errorPage; private LruCache<String, String> _realPathCache = new LruCache<String, String>(1024); // real-path mapping // private RewriteRealPath _rewriteRealPath; // mime mapping private HashMap<String, String> _mimeMapping = new HashMap<String, String>(); // locale mapping private HashMap<String, String> _localeMapping = new HashMap<String, String>(); // listeners------------ // List of the ServletContextListeners from the configuration file private ArrayList<ServletContextListener> _contextListeners = new ArrayList<ServletContextListener>(); // List of the ServletContextAttributeListeners from the configuration file private ArrayList<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<ServletContextAttributeListener>(); // List of the ServletRequestListeners from the configuration file private ArrayList<ServletRequestListener> _requestListeners = new ArrayList<ServletRequestListener>(); // List of the ServletRequestAttributeListeners from the configuration file private ArrayList<ServletRequestAttributeListener> _requestAttributeListeners = new ArrayList<ServletRequestAttributeListener>(); // listeners----------------------------------------------- // WebApp的根目录 private final String _rootDirectory; private String _tempDir; private boolean _cookieHttpOnly; private HashMap<String, Object> _extensions = new HashMap<String, Object>(); private boolean _isEnabled = true; // The session manager private SessionManager _sessionManager; private String _characterEncoding; /** * true: use a separate WebAppClassLoader for separate WebApp false: add class path of the WebApp to * SystemClassLoader(sun.misc.Launcher.AppClassLoader) by reflect false: only use for single WebApp mode */ boolean useWebAppClassLoader = true; /** * Creates the webApp with its environment loader. */ public WebApp(String rootDirectory, String host, String contextPath) { _rootDirectory = rootDirectory; _host = host; if (contextPath == null || contextPath.equals("ROOT") || contextPath.equals("/")) // ROOT Context _contextPath = ""; else _contextPath = contextPath; if (_host == null) throw new IllegalStateException("requires host"); if (useWebAppClassLoader) _classLoader = createWebAppClassLoader(); else { try { _classLoader = addClassPathToSystemClassLoader(); } catch (Exception e) { e.printStackTrace(); } } // if (debug) // displayClassLoader(); Thread.currentThread().setContextClassLoader(getClassLoader()); _uriDecoder = new URIDecoder(); initConstructor(); } /** * 使用自定义的类加载器,将/WEB-INF/classes和/WEB-INF/lib/*.jar加入类加载器的classPath */ protected WebAppClassLoader createWebAppClassLoader() { WebAppClassLoader webAppClassLoader = null; try { ClassLoader parent = Thread.currentThread().getContextClassLoader(); if (parent == null) parent = WebApp.class.getClassLoader(); webAppClassLoader = new WebAppClassLoader(parent); } catch (IOException e) { e.printStackTrace(); } List<URL> urls = new ArrayList<URL>(); // JarFile: "/WEB-INF/lib" File libPath = new File(getRealPath("/WEB-INF/lib")); if (libPath.isDirectory()) { for (File file : libPath.listFiles()) { if (file.getPath().endsWith(".jar")) { try { URL url = file.toURI().toURL(); urls.add(url); } catch (MalformedURLException e) { e.printStackTrace(); } } } } for (URL jarFile : urls) { webAppClassLoader.addJar(jarFile); } // ClassPath: "/WEB-INF/classes/" URL classPath = null; try { classPath = super.getResource("/WEB-INF/classes/"); } catch (MalformedURLException e) { e.printStackTrace(); } if (classPath != null) { webAppClassLoader.addClassPath(classPath); } return webAppClassLoader; } /** * add class path of the WebApp to SystemClassLoader(sun.misc.Launcher.AppClassLoader) by reflect NOTICE: only use * for single WebApp mode * * 因为如果使用上面的webAppClassLoader,有些情况下,对于外部的模块,它不一定严格使用ServletContext的类加载器(这里即webAppClassLoader)来加载, * 也不一定使用绑定到线程的Thread#ContextClassLoader来加载,这时候,很容易出现ClassNotFoundException. * * 利用反射,将/WEB-INF/classes和/WEB-INF/lib/*.jar加入到SystemClassLoader中 * * @throws Exception * * */ protected ClassLoader addClassPathToSystemClassLoader() throws Exception { URLClassLoader sysClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); // 取得 Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); // 压制JAVA访问检查,来直接访问其私有方法 List<URL> urls = new ArrayList<URL>(); // JarFile: "/WEB-INF/lib" File libPath = new File(getRealPath("/WEB-INF/lib")); if (libPath.isDirectory()) { for (File file : libPath.listFiles()) { if (file.getPath().endsWith(".jar")) { URL url = file.toURI().toURL(); urls.add(url); } } } for (URL jarFile : urls) { // sysClassLoader.classPath(jarFile); method.invoke(sysClassLoader, jarFile); } // ClassPath: "/WEB-INF/classes/" URL classPath = super.getResource("/WEB-INF/classes/"); if (classPath != null) { // sysClassLoader.classPath(classPath); method.invoke(sysClassLoader, classPath); } return sysClassLoader; } void displayClassLoader() { log.debug("BootstrapClassLoader 的加载路径: "); URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (URL url : urls) log.debug(url); log.debug("----------------------------"); // 取得扩展类加载器 URLClassLoader extClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader().getParent(); log.debug(extClassLoader); log.debug("扩展类加载器 的加载路径: "); urls = extClassLoader.getURLs(); for (URL url : urls) log.debug(url); log.debug("----------------------------"); // 取得应用(系统)类加载器 URLClassLoader appClassLoader = (URLClassLoader) _classLoader.getParent(); log.debug(appClassLoader); log.debug("应用(系统)类加载器 的加载路径: "); urls = appClassLoader.getURLs(); for (URL url : urls) log.debug(url); log.debug("----------------------------"); // 取得应用(系统)类加载器 appClassLoader = (URLClassLoader) _classLoader; log.debug(appClassLoader); log.debug("应用(系统)类加载器 的加载路径: "); urls = appClassLoader.getURLs(); for (URL url : urls) log.debug(url); log.debug("----------------------------"); } private void initConstructor() { _beanFactory = new BeanFactory(getClassLoader()); _servletManager = new ServletManager(this); _servletMapper = new ServletMapper(this, this, _servletManager); _filterManager = new FilterManager(this, this); _dispatchFilterMapper = new FilterMapper(this, _filterManager, DispatcherType.REQUEST); _includeFilterMapper = new FilterMapper(this, _filterManager, DispatcherType.INCLUDE); _forwardFilterMapper = new FilterMapper(this, _filterManager, DispatcherType.FORWARD); _errorFilterMapper = new FilterMapper(this, _filterManager, DispatcherType.ERROR); // _errorPageManager = new ErrorPageManager(_server, this); // Use JVM temp dir as ServletContext temp dir. _tempDir = System.getProperty(TEMPDIR); _sessionManager = new SessionManager(this); } /** * Gets the webApp directory. */ @Override public String getRootDirectory() { return _rootDirectory; } /** * Returns the webApp's canonical context path, e.g. /foo-1.0 */ @Override public String getContextPath() { return _contextPath; } void setContextPath(String contextPath) { // server/1h10 _contextPath = contextPath; } /** * Returns the owning host. */ public String getHost() { return _host; } public URIDecoder getURIDecoder() { return _uriDecoder; } /** * Gets the environment class loader. */ @Override public ClassLoader getClassLoader() { if (_classLoader == null) { _classLoader = Thread.currentThread().getContextClassLoader(); if (_classLoader == null) _classLoader = this.getClass().getClassLoader(); } return _classLoader; } /** * Sets the servlet version. */ public void setVersion(String version) { _servletVersion = version; } /** * Returns the servlet version. */ public String getVersion() { return _servletVersion; } public void setEnabled(boolean isEnabled) { _isEnabled = isEnabled; } public boolean isEnabled() { return _isEnabled; } /** * Gets the URL */ public String getURL() { return getContextPath(); } /** * Gets the URL */ public String getHostName() { return _hostName; } /** * Adds a servlet configuration. */ public void addServlet(ServletConfigImpl config) throws ServletException { checkServlerConfig(config); _servletManager.addServlet(config); } /** * 检查ServletConfigImpl里的webApp,servletContext,ServletManager是否符合本WebApp里的 * * @param config */ private void checkServlerConfig(ServletConfigImpl config) { Assert.notNull(config); Assert.isTrue(config.getWebApp() == this); Assert.isTrue(config.getServletContext() == this); Assert.isTrue(config.getServletManager() == this._servletManager); Assert.isTrue(config.getServletMapper() == this.getServletMapper()); } /** * 检查ServletConfigImpl里的webApp,servletContext,ServletManager是否符合本WebApp里的 * * @param config */ private void checkServletMapping(ServletMapping servletMapping) { Assert.notNull(servletMapping); checkServlerConfig(servletMapping.getServletConfig()); } @Override public <T extends Servlet> T createServlet(Class<T> servletClass) throws ServletException { return _beanFactory.createBean(servletClass); } @Override public ServletRegistration.Dynamic addServlet(String servletName, String className) { Class<? extends Servlet> servletClass; try { servletClass = (Class) Class.forName(className, false, getClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(className + " is an unknown class in " + this, e); } return addServlet(servletName, className, servletClass, null); } @Override public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) { return addServlet(servletName, servletClass.getName(), servletClass, null); } @Override public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { Class cl = servlet.getClass(); return addServlet(servletName, cl.getName(), cl, servlet); } /** * Adds a new or augments existing registration * * @since 3.0 */ private ServletRegistration.Dynamic addServlet(String servletName, String servletClassName, Class<? extends Servlet> servletClass, Servlet servlet) { try { ServletConfigImpl config = (ServletConfigImpl) getServletRegistration(servletName); if (config == null) { config = createNewServletConfig(); config.setServletName(servletName); config.setServletClass(servletClassName); config.setServletClass(servletClass); config.setServlet(servlet); addServlet(config); } else { if (config.getClassName() == null) config.setServletClass(servletClassName); if (config.getServletClass() == null) config.setServletClass(servletClass); if (config.getServlet() == null) config.setServlet(servlet); } if (debug) { log.debug("dynamic servlet added [name: '" + servletName + "', class: '" + servletClassName + "'] (in " + this + ")"); } return config; } catch (ServletException e) { // spec declares no throws so far throw new RuntimeException(e.getMessage(), e); } } @Override public ServletRegistration getServletRegistration(String servletName) { return _servletManager.getServlet(servletName); } @Override public Map<String, ServletRegistration> getServletRegistrations() { Map<String, ServletConfigImpl> configMap = _servletManager.getServlets(); Map<String, ServletRegistration> result = new HashMap<String, ServletRegistration>(configMap); return Collections.unmodifiableMap(result); } @Override public <T extends Filter> T createFilter(Class<T> filterClass) throws ServletException { return _beanFactory.createBean(filterClass); } @Override public FilterRegistration.Dynamic addFilter(String filterName, String className) { return addFilter(filterName, className, null, null); } @Override public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) { return addFilter(filterName, filterClass.getName(), filterClass, null); } @Override public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { Class cl = filter.getClass(); return addFilter(filterName, cl.getName(), cl, filter); } /** * * 增加Filter * * @param filterName * @param className * @param filterClass * @param filter * @return */ private FilterRegistration.Dynamic addFilter(String filterName, String className, Class<? extends Filter> filterClass, Filter filter) { try { FilterConfigImpl config = createNewFilterConfig(); config.setFilterName(filterName); config.setFilterClass(className); if (filterClass != null) config.setFilterClass(filterClass); if (filter != null) config.setFilter(filter); addFilter(config); return config; } catch (ClassNotFoundException e) { e.printStackTrace(); // spec declares no throws so far. throw new RuntimeException(e.getMessage(), e); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Returns the character encoding. */ public String getCharacterEncoding() { return _characterEncoding; } /** * 创建一个新的ServletConfigImpl * * @return */ public ServletConfigImpl createNewServletConfig() { ServletConfigImpl config = new ServletConfigImpl(this, this, _servletManager, _servletMapper); return config; } /** * 创建一个新的ServletMapping * * @return */ public ServletMapping createNewServletMapping(ServletConfigImpl config) { checkServlerConfig(config); ServletMapping servletMapping = new ServletMapping(config); return servletMapping; } /** * 创建一个新的ServletConfigImpl * * @return */ public FilterConfigImpl createNewFilterConfig() { FilterConfigImpl config = new FilterConfigImpl(this, this, _filterManager); return config; } /** * 创建一个新的ServletMapping * * @return */ public FilterMapping createNewFilterMapping(FilterConfigImpl config) { checkFilterConfig(config); FilterMapping servletMapping = new FilterMapping(config); return servletMapping; } /** * Adds a servlet-mapping configuration. */ public void addServletMapping(ServletMapping servletMapping) throws ServletException { checkServletMapping(servletMapping); _servletMapper.addServletMapping(servletMapping); } /** * Adds a filter configuration. * * @throws ServletException */ public void addFilter(FilterConfigImpl config) throws ServletException { checkFilterConfig(config); _filterManager.addFilter(config); } /** * 检查FilterConfigImpl里的webApp,servletContext,FilterManager是否符合本WebApp里的 * * @param config */ private void checkFilterConfig(FilterConfigImpl config) { Assert.notNull(config); Assert.isTrue(config.getWebApp() == this); Assert.isTrue(config.getServletContext() == this); Assert.isTrue(config.getFilterManager() == this.getFilterManager()); } /** * 检查FilterMapping里的各属性是否符合本WebApp的属性 * * @param filterMapping */ private void checkFilterMapping(FilterMapping filterMapping) { Assert.notNull(filterMapping); checkFilterConfig(filterMapping.getFilterConfig()); } /** * Adds a filter-mapping configuration. * * 添加一个FilterMapping,相当于添加一个web.xml的<filter-mapping>标签 */ public void addFilterMapping(FilterMapping filterMapping) throws ServletException { checkFilterMapping(filterMapping); _filterManager.addFilterMapping(filterMapping); // 按DispatcherType分类存放 if (filterMapping.isRequest()) _dispatchFilterMapper.addFilterMapping(filterMapping); if (filterMapping.isInclude()) _includeFilterMapper.addFilterMapping(filterMapping); if (filterMapping.isForward()) _forwardFilterMapper.addFilterMapping(filterMapping); if (filterMapping.isError()) _errorFilterMapper.addFilterMapping(filterMapping); } @Override public FilterRegistration getFilterRegistration(String filterName) { return _filterManager.getFilter(filterName); } /** * Returns filter registrations * * @return */ @Override public Map<String, ? extends FilterRegistration> getFilterRegistrations() { Map<String, FilterConfigImpl> configMap = _filterManager.getFilters(); Map<String, FilterRegistration> result = new HashMap<String, FilterRegistration>(configMap); return Collections.unmodifiableMap(result); } /** * Configures the session manager. */ public SessionManager createSessionConfig() throws Exception { SessionManager manager = getSessionManager(); return manager; } /** * Adds the session manager. */ public void addSessionConfig(SessionManager manager) throws ConfigException { } /** * Sets the cookie-http-only */ public void setCookieHttpOnly(boolean isHttpOnly) { _cookieHttpOnly = isHttpOnly; } /** * Sets the cookie-http-only */ public boolean getCookieHttpOnly() { return _cookieHttpOnly; } /** * Adds a mime-mapping */ public void addMimeMapping(String _extension, String _mimeType) { _mimeMapping.put(_extension, _mimeType); } /** * Adds a locale-mapping */ public void putLocaleEncoding(String locale, String encoding) { _localeMapping.put(locale.toLowerCase(Locale.ENGLISH), encoding); } /** * Sets the secure requirement. */ public void setSecure(boolean isSecure) { _isSecure = isSecure; } public boolean isSecure() { return _isSecure; } public Boolean isRequestSecure() { return isSecure(); } @Override public <T extends EventListener> T createListener(Class<T> listenerClass) throws ServletException { return _beanFactory.createBean(listenerClass); } @Override public void addListener(String className) { try { Class listenerClass = Class.forName(className, false, getClassLoader()); addListener(listenerClass); } catch (ClassNotFoundException e) { throw ConfigException.create(e); } } @Override public void addListener(Class<? extends EventListener> listenerClass) { addListener(_beanFactory.createBean(listenerClass)); } @Override public <T extends EventListener> void addListener(T listener) { addListenerObject(listener, false); } /** * Adds the listener object. */ private void addListenerObject(Object listenerObj, boolean start) { // ServletContextListener if (listenerObj instanceof ServletContextListener) { ServletContextListener scListener = (ServletContextListener) listenerObj; _contextListeners.add(scListener); // 发布 ServletContextEvent#contextInitialized 事件 if (start) { ServletContextEvent event = new ServletContextEvent(this); try { scListener.contextInitialized(event); } catch (Exception e) { e.printStackTrace(); log.debug(e.toString(), e); } } } // ServletContextAttributeListener if (listenerObj instanceof ServletContextAttributeListener) addAttributeListener((ServletContextAttributeListener) listenerObj); // ServletRequestListener if (listenerObj instanceof ServletRequestListener) { _requestListeners.add((ServletRequestListener) listenerObj); } // ServletRequestAttributeListener if (listenerObj instanceof ServletRequestAttributeListener) { _requestAttributeListeners.add((ServletRequestAttributeListener) listenerObj); } // HttpSessionListener if (listenerObj instanceof HttpSessionListener) getSessionManager().addListener((HttpSessionListener) listenerObj); // HttpSessionListener if (listenerObj instanceof HttpSessionAttributeListener) getSessionManager().addAttributeListener((HttpSessionAttributeListener) listenerObj); // HttpSessionActivationListener if (listenerObj instanceof HttpSessionActivationListener) getSessionManager().addActivationListener((HttpSessionActivationListener) listenerObj); } /** * Returns the request listeners. */ public List<ServletRequestListener> getRequestListeners() { return _requestListeners; } /** * Returns the request attribute listeners. */ public List<ServletRequestAttributeListener> getRequestAttributeListeners() { return _requestAttributeListeners; } // special config /** * Sets the temporary directory */ public void setTempDir(String path) { _tempDir = path; } /** * Returns an extension. */ public Object getExtension(String key) { return _extensions.get(key); } /** * Returns true if should ignore client disconnect. */ public boolean isIgnoreClientDisconnect() { return true; } /** * Initializes. */ @PostConstruct public void init() { log.debug("Initializing."); // setAttribute("javax.servlet.context.tempdir", new File(_tempDir)); setAttribute(InstanceManager.class.getName(), _beanFactory); _characterEncoding = "utf-8"; } public void parseWebXml() throws ServletException { log.debug("parseing webXml."); WebXmlLoader loader = new WebXmlLoader(this); try { // 加载 <context-param>参数 loader.loadInitParam(); // 加载<listener>标签 loader.loadListener(); // 解释web.xml所有的<filter>元素 LinkedHashMap<String, FilterConfigImpl> filterConfigMap = loader.praseFilter(); for (Entry<String, FilterConfigImpl> e : filterConfigMap.entrySet()) { addFilter(e.getValue()); } // 解释web.xml所有的<filter-mapping>元素 loader.parseFilterMapping(filterConfigMap); // 解释web.xml中的<servlet>标签 LinkedHashMap<String, ServletConfigImpl> servletConfigMap = loader.praseServletConfig(); // 解释web.xml中的<servlet-mapping>标签 loader.parseServletMapping(servletConfigMap); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * Start App */ public void start() { log.debug(" Starting."); try { // 加载web.xml parseWebXml(); configJsp(); // publishContextInitializedEvent(); // Servlet 3.0 try { _filterManager.init(); _servletManager.init(); } catch (Exception e) { throw e; } clearCache(); } catch (Exception e) { throw ConfigException.create(e); } finally { } } /** * 配置JSP相关的 JspServletComposite的ServletConfig配置信息 * * @throws ServletException */ private void configJsp() throws ServletException { ServletConfigImpl config = createNewServletConfig(); config.setServletName(JspServletComposite.class.getCanonicalName()); config.setServletClass(JspServletComposite.class); // 缺省情况下,关闭development模式,提高性能 config.setInitParameter("development", "false"); _servletManager.addServlet(config); } /* ------------------------------------------------------------ */ /** * 发布publish ContextInitialized Event 事件 */ protected void publishContextInitializedEvent() { log.debug("publish ContextInitialized Event"); // 发布ServletContextListener#contextInitialized事件 ServletContextEvent event = new ServletContextEvent(this); for (int i = 0; i < _contextListeners.size(); i++) { ServletContextListener listener = _contextListeners.get(i); try { listener.contextInitialized(event); } catch (Exception e) { log.warn(e.toString(), e); } } } /** * Returns the servlet context for the URI. */ @Override public ServletContext getContext(String uri) { if (uri == null) throw new IllegalArgumentException("getContext URI must not be null."); else if (uri.startsWith("/")) { } else if (uri.equals("")) uri = "/"; else throw new IllegalArgumentException("getContext URI '" + uri + "' must be absolute."); return this; } public ServletMapper getServletMapper() { return _servletMapper; } /** * Clears all caches, including the invocation cache, the filter cache, and the proxy cache. */ public void clearCache() { _requestDispatcherCache = null; // server/1kg1 synchronized (_dispatchFilterChainCache) { _dispatchFilterChainCache.clear(); } synchronized (_forwardFilterChainCache) { _forwardFilterChainCache.clear(); } synchronized (_includeFilterChainCache) { _includeFilterChainCache.clear(); } synchronized (_errorFilterChainCache) { _errorFilterChainCache.clear(); } } /** * Build a invocation for a dispatch request * * 给Dispatch类型的rawContextURI创建一个HttpInvocation * */ public HttpInvocation buildDispatchInvocation(String rawContextURI) throws ServletException { HttpInvocation invocation = new HttpInvocation(this, rawContextURI); //分解URI _uriDecoder.splitQuery(invocation, rawContextURI); String contextURI = invocation.getContextURI(); // try to get from Cache FilterChainInvocation fcInvocation = _dispatchFilterChainCache.get(contextURI); //Found if (fcInvocation != null) { invocation.setFilterChainInvocation(fcInvocation); return invocation; } //Not Found,So build it fcInvocation = createFilterChainInvocation(contextURI,_dispatchFilterMapper); // 创建带有ServletRequestListener通知功能的FilterChain, Build FilterChain for the notification of ServletRequestListener(s), if (_requestListeners != null && _requestListeners.size() > 0) { //Old Chain FilterChain filterChain = fcInvocation.getFilterChain(); //wraped chain filterChain = new ServletRequestListenerFilterChain(filterChain, this, _requestListeners); fcInvocation.setFilterChain(filterChain); } // put to cache if (fcInvocation.getFilterChain() != null) { _dispatchFilterChainCache.put(fcInvocation.getContextURI(), fcInvocation); } invocation.setFilterChainInvocation(fcInvocation); return invocation; } /** * Build a invocation for a dispatch request * * 给Forward类型的rawContextURI创建一个HttpInvocation * */ public HttpInvocation buildForwardInvocation(String rawContextURI) throws ServletException { HttpInvocation invocation = new HttpInvocation(this, rawContextURI); //分解URI _uriDecoder.splitQuery(invocation, rawContextURI); String contextURI = invocation.getContextURI(); // try to get from Cache FilterChainInvocation fcInvocation = _forwardFilterChainCache.get(contextURI); //Found if (fcInvocation != null) { invocation.setFilterChainInvocation(fcInvocation); return invocation; } //Not Found,So build it fcInvocation = createFilterChainInvocation(contextURI,_forwardFilterMapper); // put to cache if (fcInvocation.getFilterChain() != null) { _forwardFilterChainCache.put(fcInvocation.getContextURI(), fcInvocation); } invocation.setFilterChainInvocation(fcInvocation); return invocation; } /** * Build a invocation for a dispatch request * * 给Include类型的rawContextURI创建一个HttpInvocation * */ public HttpInvocation buildIncludeInvocation(String rawContextURI) throws ServletException { HttpInvocation invocation = new HttpInvocation(this, rawContextURI); //分解URI _uriDecoder.splitQuery(invocation, rawContextURI); String contextURI = invocation.getContextURI(); // try to get from Cache FilterChainInvocation fcInvocation = _includeFilterChainCache.get(contextURI); //Found if (fcInvocation != null) { invocation.setFilterChainInvocation(fcInvocation); return invocation; } //Not Found,So build it fcInvocation = createFilterChainInvocation(contextURI,_includeFilterMapper); // put to cache if (fcInvocation.getFilterChain() != null) { _includeFilterChainCache.put(fcInvocation.getContextURI(), fcInvocation); } invocation.setFilterChainInvocation(fcInvocation); return invocation; } /** * Build a invocation for a dispatch request * * 给Error类型的rawContextURI创建一个HttpInvocation * */ public HttpInvocation buildErrorInvocation(String rawContextURI) throws ServletException { HttpInvocation invocation = new HttpInvocation(this, rawContextURI); //分解URI _uriDecoder.splitQuery(invocation, rawContextURI); String contextURI = invocation.getContextURI(); // try to get from Cache FilterChainInvocation fcInvocation = _errorFilterChainCache.get(contextURI); //Found if (fcInvocation != null) { invocation.setFilterChainInvocation(fcInvocation); return invocation; } //Not Found,So build it fcInvocation = createFilterChainInvocation(contextURI,_errorFilterMapper); // put to cache if (fcInvocation.getFilterChain() != null) { _errorFilterChainCache.put(fcInvocation.getContextURI(), fcInvocation); } invocation.setFilterChainInvocation(fcInvocation); return invocation; } /** * * 给特定的ContextURI创建匹配的FilterChainInvocation * * @param contextURI * @param filterMapper 与特定DispatcherType匹配的FilterMapper * @return * @throws ServletException */ private FilterChainInvocation createFilterChainInvocation(String contextURI, FilterMapper filterMapper) throws ServletException { if (debug) log.debug("createFilterChainInvocation:" + contextURI); FilterChainInvocation fcInvocation = new FilterChainInvocation(this,contextURI); try { FilterChain chain; if (!isEnabled()) //503 { Exception ex = new UnavailableException("'" + getContextPath() + "' is not currently available."); chain = new ExceptionFilterChain(ex); } else { //匹配Servlet chain = _servletMapper.buildServletChain(fcInvocation); // 测试了Jetty和Tomcat,就是无法找到合适的Sevlet来匹配,也要调用匹配的Filter //匹配Filter chain = filterMapper.buildFilterChain(fcInvocation, chain); } fcInvocation.setFilterChain(chain); } catch (Exception e) { log.debug(e.toString(), e); FilterChain chain = new ExceptionFilterChain(e); fcInvocation.setFilterChain(chain); } return fcInvocation; } /** * 创建用于触发ServletRequestListener相关事件的FilterChain * * @param chain * @return */ FilterChain createServletRequestListenerFilterChain(FilterChain chain) { if (getRequestListeners() != null && getRequestListeners().size() > 0) { chain = new ServletRequestListenerFilterChain(chain, this, getRequestListeners()); } return chain; } /** * Returns a dispatcher for the named servlet. * */ @Override public RequestDispatcherImpl getRequestDispatcher(String rawContextURI) { if (rawContextURI == null) throw new IllegalArgumentException("request dispatcher url can't be null."); else if (!rawContextURI.startsWith("/")) throw new IllegalArgumentException("request dispatcher url '" + rawContextURI + "' must be absolute"); // 尝试从缓存中取出RequestDispatcher RequestDispatcherImpl disp = getRequestDispatcherCache().get(rawContextURI); if (disp != null) return disp; try { // 将Invocation的创建延迟到RequestDispatcher的具体的dispatch或forward方法调用时再进行(很情况下不需要所有DispatcherType都创建) disp = new RequestDispatcherImpl(this, rawContextURI); // 缓存RequestDispatcher getRequestDispatcherCache().put(rawContextURI, disp); return disp; } catch (RuntimeException e) { throw e; } catch (Exception e) { log.debug(e.toString(), e); return null; } } private LruCache<String, RequestDispatcherImpl> getRequestDispatcherCache() { LruCache<String, RequestDispatcherImpl> cache = _requestDispatcherCache; if (cache != null) return cache; synchronized (this) { cache = new LruCache<String, RequestDispatcherImpl>(1024); _requestDispatcherCache = cache; return cache; } } /** * Returns a dispatcher for the named servlet. 返回一个转发到指定名称的Servlet的dispatcher */ @Override public RequestDispatcher getNamedDispatcher(String servletName) { //not support currently return null; } /** * Maps from a URI to a real path. */ /* * @Override public String getRealPath(String uri) { // server/10m7 if (uri == null) return null; * * String realPath = _realPathCache.get(uri); * * if (realPath != null) return realPath; * * WebApp webApp = this; String tail = uri; * * String fullURI = getContextPath() + "/" + uri; * * try { fullURI = getURIDecoder().normalizeUri(fullURI); } catch (Exception e) { log.warn(e.toString(), e); } * * webApp = (WebApp) getContext(fullURI); * * if (webApp == null) webApp = this; * * String cp = webApp.getContextPath(); tail = fullURI.substring(cp.length()); * * realPath = tail; * * if (debug) log.debug("real-path " + uri + " -> " + realPath); * * _realPathCache.put(uri, realPath); * * return realPath; } */ @Override public String getRealPath(String uri) { // server/10m7 if (uri == null) return null; String realPath = _realPathCache.get(uri); if (realPath != null) return realPath; realPath = super.getRealPath(uri); if (debug) log.debug("real-path " + uri + " -> " + realPath); if (realPath != null) _realPathCache.put(uri, realPath); return realPath; } /** * Returns the mime type for a uri */ @Override public String getMimeType(String uri) { if (uri == null) return null; String fullURI = getContextPath() + "/" + uri; try { fullURI = getURIDecoder().normalizeUri(fullURI); } catch (Exception e) { log.warn(e.toString(), e); } WebApp webApp = (WebApp) getContext(fullURI); if (webApp == null) return null; int p = uri.lastIndexOf('.'); if (p < 0) return null; else return webApp.getMimeTypeImpl(uri.substring(p)); } /** * Maps from a URI to a real path. */ public String getMimeTypeImpl(String ext) { return _mimeMapping.get(ext); } @Override public SessionCookieConfig getSessionCookieConfig() { return getSessionManager(); } /** * Gets the session manager. */ public SessionManager getSessionManager() { return _sessionManager; } /** * Gets the error page manager. */ public ErrorPageManager getErrorPageManager() { if (_errorPageManager == null) { _errorPageManager = new ErrorPageManager(this); } return _errorPageManager; } /** * Returns the active session count. */ public int getActiveSessionCount() { SessionManager manager = getSessionManager(); if (manager != null) return manager.getActiveSessionCount(); else return 0; } /** * Stops the webApp. */ public void stop() { long beginStop = System.currentTimeMillis(); clearCache(); ServletContextEvent event = new ServletContextEvent(this); SessionManager sessionManager = _sessionManager; _sessionManager = null; if (sessionManager != null) { sessionManager.close(); } if (_servletManager != null) _servletManager.destroy(); if (_filterManager != null) _filterManager.destroy(); // server/10g8 -- webApp listeners after session // 发布 ServletContextListener#contextDestroyed事件 publishContextDestroyedEvent(event); } /** * Closes the webApp. */ public void destroy() { try { stop(); } catch (Throwable e) { log.warn(e.toString(), e); } } /** * 发布publish ContextDestroyed Event 事件 */ protected void publishContextDestroyedEvent(ServletContextEvent event) { if (_contextListeners != null) { for (int i = _contextListeners.size() - 1; i >= 0; i--) { ServletContextListener listener = _contextListeners.get(i); try { listener.contextDestroyed(event); } catch (Exception e) { log.warn(e.toString(), e); } } } } // /static----------------------------------------------------------------------------- public FilterManager getFilterManager() { return _filterManager; } // --util----------------------------------------------------------------- /** * Error logging * * @param message * message to log * @param e * stack trace of the error */ public void log(String message, Throwable e) { if (e != null) log.warn(this + " " + message, e); else log.info(this + " " + message); } }