Java tutorial
/* * 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.dispatch.servlet; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import javax.servlet.FilterChain; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.ireland.jnetty.config.ConfigException; import org.ireland.jnetty.dispatch.FilterChainInvocation; import org.ireland.jnetty.dispatch.filterchain.ErrorFilterChain; import org.ireland.jnetty.jsp.JspServletComposite; import org.ireland.jnetty.webapp.WebApp; import org.springframework.util.Assert; import com.caucho.util.LruCache; /** * Manages dispatching: servlets and filters. :TODO: rename "ServletMapper" TO "ServletMatcher" * * Servlet? * */ public class ServletMapper { private static final Log log = LogFactory.getLog(ServletMapper.class.getName()); private static final boolean debug = log.isDebugEnabled(); private final WebApp _webApp; private final ServletContext _servletContext; private final ServletManager _servletManager; // LRUCacheContextURIServletFilterChain() private LruCache<String, FilterChain> _servletChainCache = new LruCache<String, FilterChain>(256); // urlPattern <servlet-mapping>(URL?) // 1:ServletMappings for Exact Match <urlPattern,ServletMapping> private Map<String, ServletMapping> _exactServletMappings = new HashMap<String, ServletMapping>(); // 2:ServletMappings for Longest Prefix Match <prefixPattern,ServletMapping>(pattern?) private SortedMap<String, ServletMapping> _prefixServletMappings = new TreeMap<String, ServletMapping>( new PatternLengthComparator()); // 3:ServletMappings for Extension Match <Extension,ServletMapping> (web.xml?) private LinkedHashMap<String, ServletMapping> _extensionServletMappings = new LinkedHashMap<String, ServletMapping>(); // 4:Default servlet (urlPattern"/",?Servletjsp,?Servlet) private ServletConfigImpl _defaultServlet; // ServletName urlPattern // Servlet 3.0 maps serletName to urlPattern <serletName,Set<urlPattern>> private Map<String, Set<String>> _urlPatterns = new HashMap<String, Set<String>>(); public ServletMapper(WebApp webApp, ServletContext servletContext, ServletManager servletManager) { Assert.notNull(webApp); Assert.notNull(servletContext); Assert.notNull(servletManager); _webApp = webApp; _servletContext = servletContext; _servletManager = servletManager; } // Getter and Setter--------------------------------------------------- /** * Gets the servlet context. */ public WebApp getWebApp() { return _webApp; } /** * Returns the servlet manager. */ public ServletManager getServletManager() { return _servletManager; } // Getter and Setter--------------------------------------------------- /** * Add a servletMapping * @param mapping * @throws ServletException */ public void addServletMapping(ServletMapping mapping) throws ServletException { if (mapping.getURLPatterns() != null) { for (String urlPattern : mapping.getURLPatterns()) { addUrlMapping(urlPattern, mapping); } } } /** * Adds a servlet mapping Specification: Servlet-3_1-PFD chapter 12.1 * * urlPattern + " -> " + ServletMapping * */ public void addUrlMapping(final String urlPattern, ServletMapping mapping) throws ServletException { try { ServletConfigImpl config = mapping.getServletConfig(); String servletName = config.getServletName(); //ServletConfigImpl?ServletManager, if (_servletManager.getServlet(servletName) == null) { _servletManager.addServlet(config); } if ("/".equals(urlPattern)) // Default servlet { _defaultServlet = mapping.getServletConfig(); } // ? _exactServletMappings.put(urlPattern, mapping); // ??/../* if (urlPattern.endsWith("/*")) { _prefixServletMappings.put(urlPattern, mapping); } // ??? if (urlPattern.startsWith("*.")) { _extensionServletMappings.put(urlPattern, mapping); } // Set<String> patterns = _urlPatterns.get(servletName); if (patterns == null) { patterns = new HashSet<String>(); _urlPatterns.put(servletName, patterns); } patterns.add(urlPattern); // log.debug("servlet-mapping " + urlPattern + " -> " + servletName); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } public Set<String> getUrlPatterns(String servletName) { return _urlPatterns.get(servletName); } /** * Sets the default servlet. 4. * ?servlet????default?servlet?????default servlet?? */ public void setDefaultServlet(ServletConfigImpl config) throws ServletException { _defaultServlet = config; } /** * FilterChainInvocation ? Servlet,?FilterChain * * ServletURL???URL?????? * * 1. ?servlet???servlet * * 2. ??????/?servlet * * 3. URL???? .jspservlet????Servlet????.? * * 4. ?servlet????default?servlet?????default servlet?? * * XXX:ServletFilterChain,?contextURI(??),??URI,??contextURI?,??Servlet * XXX:/login.do?u=jack,/login.do?u=ken ?ServletFilterChain * * @param fcInvocation * @return * @throws ServletException */ public FilterChain buildServletChain(FilterChainInvocation fcInvocation) throws ServletException { String contextURI = fcInvocation.getContextURI(); //?cache FilterChain servletChain = _servletChainCache.get(contextURI); if (servletChain != null) return servletChain; // ServletConfigImpl config = null; // 1-2-3:contextURI?Servlet config = mapServlet(contextURI); //3.5?jsp?? if (config == null) { config = mapJspServlet(contextURI); } // 4:Servlet(urlPattern"/",?Servletjsp,?Servlet) if (config == null) { config = _defaultServlet; } // 5:?Servlet,404 if (config == null) { if (debug) log.debug("'" + contextURI + "' has no default servlet defined"); return new ErrorFilterChain(404); } String servletPath = contextURI; // TODO: how to decide servletPath ? fcInvocation.setServletPath(servletPath); if (servletPath.length() < contextURI.length()) fcInvocation.setPathInfo(contextURI.substring(servletPath.length())); else fcInvocation.setPathInfo(null); String servletName = config.getServletName(); fcInvocation.setServletName(servletName); if (debug) log.debug(_webApp + " map (uri:" + contextURI + " -> " + servletName + ")"); // ServletFilterChain FilterChain chain = null; if (config != null) chain = _servletManager.createServletChain(config, fcInvocation); //put to cache _servletChainCache.put(contextURI, chain); return chain; } /** * contextURI?Jsp,??JspServletCompositeServletConfig * @param contextURI * @return */ protected ServletConfigImpl mapJspServlet(String contextURI) { if (contextURI != null && contextURI.endsWith(".jsp")) { return _servletManager.getServlet(JspServletComposite.class.getCanonicalName()); } return null; } /** * contextURI?Servlet Specification: Servlet-3_1-PFD chapter 12.1 * * @param contextURI * @return */ public ServletConfigImpl mapServlet(String contextURI) { // Rule 1 -- Exact Match :?URL?<servler-mapping> if (_exactServletMappings.size() > 0) { ServletMapping servletMapping = _exactServletMappings.get(contextURI); if (servletMapping != null) return servletMapping.getServletConfig(); } // Rule 2 -- Longest Prefix Match : ??Servlet,:/page/today/123 ? /page/today/*,?/page/* if (_prefixServletMappings.size() > 0) { String prefixPattern; ServletMapping servletMapping = null; for (Map.Entry<String, ServletMapping> entry : _prefixServletMappings.entrySet()) { prefixPattern = entry.getKey(); if (prefixPatternMatch(contextURI, prefixPattern)) { servletMapping = entry.getValue(); if (servletMapping != null) return servletMapping.getServletConfig(); } } } // Rule 3 -- Extension Match : ?.do??? if (_extensionServletMappings.size() > 0) { String extensionPattern; ServletMapping servletMapping = null; for (Map.Entry<String, ServletMapping> entry : _extensionServletMappings.entrySet()) { extensionPattern = entry.getKey(); if (extensionPatternMatch(contextURI, extensionPattern)) { servletMapping = entry.getValue(); if (servletMapping != null) return servletMapping.getServletConfig(); } } } return null; } public ServletMapping getServletMapping(String pattern) { return _exactServletMappings.get(pattern); } private void addServlet(String servletName) throws ServletException { if (_servletManager.getServlet(servletName) != null) return; ServletConfigImpl config = _webApp.createNewServletConfig(); try { config.setServletClass(servletName); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new ServletException(e); } config.init(); _servletManager.addServlet(config); } public void destroy() { _servletManager.destroy(); } /** * * String? * * @author KEN * */ private static class PatternLengthComparator implements Comparator<String> { @Override public int compare(String pattern1, String pattern2) { if (pattern1.length() > pattern2.length()) return -1; else if (pattern1.length() == pattern2.length()) return 0; else return 1; } } // util Method--------------------------------------------------------------------------- /** * ?? * * @param requestPath * @param prefixPattern * @return */ private static boolean prefixPatternMatch(String requestPath, String prefixPattern) { if (prefixPattern == null) return (false); // Case 2 - Path Match ("/.../*") if (prefixPattern.equals("/*")) return (true); if (prefixPattern.endsWith("/*")) { if (prefixPattern.regionMatches(0, requestPath, 0, prefixPattern.length() - 2)) { if (requestPath.length() == (prefixPattern.length() - 2)) { return (true); } else if ('/' == requestPath.charAt(prefixPattern.length() - 2)) { return (true); } } return (false); } return (false); } /** * * @param requestPath * @param extensionPattern * @return */ private static boolean extensionPatternMatch(String requestPath, String extensionPattern) { if (extensionPattern == null) return (false); // Case 3 - Extension Match if (extensionPattern.startsWith("*.")) { int slash = requestPath.lastIndexOf('/'); int period = requestPath.lastIndexOf('.'); if ((slash >= 0) && (period > slash) && (period != requestPath.length() - 1) && ((requestPath.length() - period) == (extensionPattern.length() - 1))) { return (extensionPattern.regionMatches(2, requestPath, period + 1, extensionPattern.length() - 2)); } } return (false); } }