Java tutorial
/* * Copyright 2007-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License i distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laxser.blitz; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.SpringVersion; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.filter.GenericFilterBean; import org.springframework.web.util.NestedServletException; import com.laxser.blitz.scanner.ModuleResource; import com.laxser.blitz.scanner.ModuleResourceProvider; import com.laxser.blitz.scanner.ModuleResourceProviderImpl; import com.laxser.blitz.scanning.LoadScope; import com.laxser.blitz.scanning.context.BlitzWebAppContext; import com.laxser.blitz.util.PrinteHelper; import com.laxser.blitz.web.RequestPath; import com.laxser.blitz.web.annotation.ReqMethod; import com.laxser.blitz.web.impl.mapping.ConstantMapping; import com.laxser.blitz.web.impl.mapping.Mapping; import com.laxser.blitz.web.impl.mapping.MappingNode; import com.laxser.blitz.web.impl.mapping.TreeBuilder; import com.laxser.blitz.web.impl.mapping.ignored.IgnoredPath; import com.laxser.blitz.web.impl.mapping.ignored.IgnoredPathEnds; import com.laxser.blitz.web.impl.mapping.ignored.IgnoredPathEquals; import com.laxser.blitz.web.impl.mapping.ignored.IgnoredPathRegexMatch; import com.laxser.blitz.web.impl.mapping.ignored.IgnoredPathStarts; import com.laxser.blitz.web.impl.module.Module; import com.laxser.blitz.web.impl.module.ModulesBuilder; import com.laxser.blitz.web.impl.module.ModulesBuilderImpl; import com.laxser.blitz.web.impl.thread.LinkedEngine; import com.laxser.blitz.web.impl.thread.RootEngine; import com.laxser.blitz.web.impl.thread.Blitz; import com.laxser.blitz.web.instruction.InstructionExecutor; import com.laxser.blitz.web.instruction.InstructionExecutorImpl; /** * Blitz Servlet?Spring?WEB? * <p> * Blitz web.xml???webBlitz?? * Blitz??. * Blitz???Blitzweb?? * <p> * * Blitz?Servlet?web??? * <p> * Servlet???? * ?web?web.xmlServlet??webFilter * Filter?RequestResponse * Filter???????. * <p> * Blitz?web????uri? * Blitz???? * <p> * ??Blitz??FilterServletServletServlet? * ????? * Servlet??Servlet????web.xml? * (404500?) * <p> * * web.xml???FilterFilter?????Blitz??? * Blitz???mapping?Blitz? * <p> * * BlitzFilter???? * * <pre> * <filter> * <filter-name>BlitzFilter</filter-name> * <filter-class>net.paoding.Blitz.BlitzFilter</filter-class> * </filter> * <filter-mapping> * <filter-name>BlitzFilter</filter-name> * <url-pattern>/*</url-pattern> * <dispatcher>REQUEST</dispatcher> * <dispatcher>FORWARD</dispatcher> * <dispatcher>INCLUDE</dispatcher> * </filter-mapping> * </pre> * * 1) <strong>filter-mapping</strong> ?Filter Mapping?<br> * 2) ? <strong>FORWARD?INCLUDE</strong> dispatcher ?forward? * includeBlitz?<br> * <p> * * Blitz<strong>"?->"</strong>Blitz? * ???Blitz? ????? * ??6?<strong>?? - ? - - - * -?"</strong>? * <p> * * <strong>?</strong>: <br> * TODO * <p> * * <strong>?</strong>: <br> * TODO * <P> * * <strong>??</strong>: <br> * ?? * ?Blitz?2??????????( * )??Blitz??? * <P> * * <strong></strong>: <br> * Blitz????Blitz????Blitz * ??????????? * <P> * * <strong></strong>: <br> * * */ public class BlitzFilter extends GenericFilterBean { private static final String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; /** applicationContext? */ private String contextConfigLocation; private InstructionExecutor instructionExecutor = new InstructionExecutorImpl(); private List<Module> modules; private MappingNode mappingTree; private Class<? extends ModuleResourceProvider> moduleResourceProviderClass = ModuleResourceProviderImpl.class; private Class<? extends ModulesBuilder> modulesBuilderClass = ModulesBuilderImpl.class; private LoadScope load = new LoadScope("", "controllers"); private IgnoredPath[] ignoredPaths = new IgnoredPath[] { new IgnoredPathStarts(BlitzConstants.VIEWS_PATH_WITH_END_SEP), new IgnoredPathEquals("/favicon.ico") }; /** * ?Blitz??applicationContext? */ public void setContextConfigLocation(String contextConfigLocation) { if (StringUtils.isBlank(contextConfigLocation)) { throw new IllegalArgumentException("contextConfigLocation"); } this.contextConfigLocation = contextConfigLocation; } public void setInstructionExecutor(InstructionExecutor instructionExecutor) { this.instructionExecutor = instructionExecutor; } public void setModuleResourceProviderClass( Class<? extends ModuleResourceProvider> moduleResourceProviderClass) { this.moduleResourceProviderClass = moduleResourceProviderClass; } public void setModulesBuilderClass(Class<? extends ModulesBuilder> modulesBuilderClass) { this.modulesBuilderClass = modulesBuilderClass; } /** * <pre> * like: "com.renren.myapp, com.renren.yourapp" etc * </pre> * * @param load */ public void setLoad(String load) { this.load = new LoadScope(load, "controllers"); } /** * @see #quicklyPass(RequestPath) * @param ignoredPathStrings */ public void setIgnoredPaths(String[] ignoredPathStrings) { List<IgnoredPath> list = new ArrayList<IgnoredPath>(ignoredPathStrings.length + 2); for (String ignoredPath : ignoredPathStrings) { ignoredPath = ignoredPath.trim(); if (StringUtils.isEmpty(ignoredPath)) { continue; } if (ignoredPath.equals("*")) { list.add(new IgnoredPathEquals("")); list.add(new IgnoredPathStarts("/")); break; } if (ignoredPath.startsWith("regex:")) { list.add(new IgnoredPathRegexMatch(ignoredPath.substring("regex:".length()))); } else { if (ignoredPath.length() > 0 && !ignoredPath.startsWith("/") && !ignoredPath.startsWith("*")) { ignoredPath = "/" + ignoredPath; } if (ignoredPath.endsWith("*")) { list.add(new IgnoredPathStarts(ignoredPath.substring(0, ignoredPath.length() - 1))); } else if (ignoredPath.startsWith("*")) { list.add(new IgnoredPathEnds(ignoredPath.substring(1))); } else { list.add(new IgnoredPathEquals(ignoredPath)); } } } IgnoredPath[] ignoredPaths = Arrays.copyOf(this.ignoredPaths, this.ignoredPaths.length + list.size()); for (int i = this.ignoredPaths.length; i < ignoredPaths.length; i++) { ignoredPaths[i] = list.get(i - this.ignoredPaths.length); } this.ignoredPaths = ignoredPaths; } /** * {@link GenericFilterBean#initFilterBean()} Blitz ? */ @Override protected final void initFilterBean() throws ServletException { try { if (logger.isInfoEnabled()) { logger.info("[init] call 'init/rootContext'"); } if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); Enumeration<String> iter = getFilterConfig().getInitParameterNames(); while (iter.hasMoreElements()) { String name = (String) iter.nextElement(); sb.append(name).append("='").append(getFilterConfig().getInitParameter(name)).append("'\n"); } logger.debug("[init] parameters: " + sb); } WebApplicationContext rootContext = prepareRootApplicationContext(); if (logger.isInfoEnabled()) { logger.info("[init] exits from 'init/rootContext'"); logger.info("[init] call 'init/module'"); } // Blitz ?? this.modules = prepareModules(rootContext); if (logger.isInfoEnabled()) { logger.info("[init] exits from 'init/module'"); logger.info("[init] call 'init/mappingTree'"); } // ???(Engine) this.mappingTree = prepareMappingTree(modules); if (logger.isInfoEnabled()) { logger.info("[init] exits from 'init/mappingTree'"); logger.info("[init] exits from 'init'"); } // ??? printBlitzInfos(); // } catch (final Throwable e) { StringBuilder sb = new StringBuilder(1024); sb.append("[Blitz-").append(BlitzVersion.getVersion()); sb.append("@Spring-").append(SpringVersion.getVersion()).append("]:"); sb.append(e.getMessage()); logger.error(sb.toString(), e); throw new NestedServletException(sb.toString(), e); } } /** * BlitzFilter ???????? * ??Blitzweb?? */ @Override public void doFilter(ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { // cast final HttpServletRequest httpRequest = (HttpServletRequest) request; final HttpServletResponse httpResponse = (HttpServletResponse) response; // DEBUG?BlitzFilter if (logger.isDebugEnabled()) { StringBuffer sb = httpRequest.getRequestURL(); String query = httpRequest.getQueryString(); if (query != null && query.length() > 0) { sb.append("?").append(query); } logger.debug(httpRequest.getMethod() + " " + sb.toString()); } supportsBlitzpipe(httpRequest); // RequestPath?? final RequestPath requestPath = new RequestPath(httpRequest); // ???Blitztrue if (quicklyPass(requestPath)) { notMatched(filterChain, httpRequest, httpResponse, requestPath); return; } // matchedtrueBlitz???? flter servlet boolean matched = false; try { // Blitz Blitz? final Blitz Blitz = new Blitz(modules, mappingTree, httpRequest, httpResponse, requestPath); // ?????????false matched = Blitz.start(); } catch (Throwable exception) { throwServletException(requestPath, exception); } // ?Blitz?WEB???try-catch? if (!matched) { notMatched(filterChain, httpRequest, httpResponse, requestPath); } } // @see net.paoding.Blitz.web.portal.impl.PortalWaitInterceptor#waitForWindows protected void supportsBlitzpipe(final HttpServletRequest httpRequest) { // ?Blitzpipe??Blitzpipe"Cannot forward after response has been committed" // @see net.paoding.Blitz.web.portal.impl.PortalWaitInterceptor Object window = httpRequest.getAttribute(BlitzConstants.WINDOW_ATTR); if (window != null && window.getClass().getName().startsWith("com.laxser.blitz.web.portal")) { httpRequest.setAttribute(BlitzConstants.PIPE_WINDOW_IN, Boolean.TRUE); if (logger.isDebugEnabled()) { try { logger.debug("notify window '" + httpRequest.getAttribute("$$blitz-portal.window.name") + "'"); } catch (Exception e) { logger.error("", e); } } synchronized (window) { window.notifyAll(); } } } /** * ApplicationContext WEB-INF?WEB-INF/classes? * jarspring???? ApplicationContext * * @return * @throws IOException */ private WebApplicationContext prepareRootApplicationContext() throws IOException { if (logger.isInfoEnabled()) { logger.info("[init/rootContext] starting ..."); } ApplicationContext oldRootContext = (ApplicationContext) getServletContext() .getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); // web.xml?springrootcontext ...... ?? // BlitzFilter?????rootContext????? // ?Listenerinit Blitz context if (oldRootContext != null) { if (oldRootContext.getClass() != BlitzWebAppContext.class) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } if (logger.isInfoEnabled()) { logger.info("[init/rootContext] the root context exists:" + oldRootContext); } return (BlitzWebAppContext) oldRootContext; } BlitzWebAppContext rootContext = new BlitzWebAppContext(getServletContext(), load, false); String contextConfigLocation = this.contextConfigLocation; // applicationContext? if (StringUtils.isBlank(contextConfigLocation)) { String webxmlContextConfigLocation = getServletContext().getInitParameter("contextConfigLocation"); if (StringUtils.isBlank(webxmlContextConfigLocation)) { contextConfigLocation = BlitzWebAppContext.DEFAULT_CONFIG_LOCATION; } else { contextConfigLocation = webxmlContextConfigLocation; } } rootContext.setConfigLocation(contextConfigLocation); rootContext.setId("Blitz.root"); rootContext.refresh(); if (logger.isInfoEnabled()) { logger.info("[init/rootContext] exits"); } /* enable: WebApplicationContextUtils.getWebApplicationContext() */ getServletContext().setAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext); if (logger.isInfoEnabled()) { logger.info("[init/rootContext] Published Blitz.root WebApplicationContext [" + rootContext + "] as ServletContext attribute with name [" + ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } return rootContext; } private List<Module> prepareModules(WebApplicationContext rootContext) throws Exception { // ??web?Blitz? if (logger.isInfoEnabled()) { logger.info("[init/mudule] starting ..."); } ModuleResourceProvider provider = moduleResourceProviderClass.newInstance(); if (logger.isInfoEnabled()) { logger.info("[init/module] using provider: " + provider); logger.info("[init/module] call 'moduleResource': to find all module resources."); logger.info("[init/module] load " + load); } List<ModuleResource> moduleResources = provider.findModuleResources(load); if (logger.isInfoEnabled()) { logger.info("[init/mudule] exits 'moduleResource'"); } ModulesBuilder modulesBuilder = modulesBuilderClass.newInstance(); if (logger.isInfoEnabled()) { logger.info("[init/module] using modulesBuilder: " + modulesBuilder); logger.info("[init/module] call 'moduleBuild': to build modules."); } List<Module> modules = modulesBuilder.build(moduleResources, rootContext); if (logger.isInfoEnabled()) { logger.info("[init/module] exits from 'moduleBuild'"); logger.info("[init/mudule] found " + modules.size() + " modules."); } return modules; } private MappingNode prepareMappingTree(List<Module> modules) { Mapping rootMapping = new ConstantMapping(""); MappingNode mappingTree = new MappingNode(rootMapping); LinkedEngine rootEngine = new LinkedEngine(null, new RootEngine(instructionExecutor), mappingTree); mappingTree.getMiddleEngines().addEngine(ReqMethod.ALL, rootEngine); TreeBuilder treeBuilder = new TreeBuilder(); treeBuilder.create(mappingTree, modules); return mappingTree; } /** * ???Blitztrue * * @param requestPath * @return */ private boolean quicklyPass(final RequestPath requestPath) { for (IgnoredPath p : ignoredPaths) { if (p.hit(requestPath)) { return true; } } return false; } @Override public void destroy() { WebApplicationContext rootContext = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); if (rootContext != null) { try { if (rootContext instanceof AbstractApplicationContext) { ((AbstractApplicationContext) rootContext).close(); // Blitz.root } } catch (Throwable e) { logger.error("", e); getServletContext().log("", e); } } try { mappingTree.destroy(); } catch (Throwable e) { logger.error("", e); getServletContext().log("", e); } super.destroy(); } /** * Blitz ? Blitz???BlitzUriBlitz? * ?resp reqFilterChainFilter? * * @param filterChain * @param httpRequest * @param httpResponse * @param path * @throws IOException * @throws ServletException */ protected void notMatched(// FilterChain filterChain, // HttpServletRequest httpRequest, // HttpServletResponse httpResponse, // RequestPath path)// throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("not a Blitz uri: " + path.getUri()); } // Filter filterChain.doFilter(httpRequest, httpResponse); } /** * ? * @param requestPath * @param exception * @throws ServletException */ private void throwServletException(RequestPath requestPath, Throwable exception) throws ServletException { String msg = requestPath.getMethod() + " " + requestPath.getUri(); ServletException servletException; if (exception instanceof ServletException) { servletException = (ServletException) exception; } else { servletException = new NestedServletException(msg, exception); } logger.error(msg, exception); getServletContext().log(msg, exception); throw servletException; } private void printBlitzInfos() { if (logger.isDebugEnabled()) { logger.debug(PrinteHelper.dumpModules(modules)); logger.debug("Mapping tree:\n" + PrinteHelper.list(mappingTree)); } String msg = String.format("[init] Blitz initialized, %s modules loaded! (version=%s)", modules.size(), BlitzVersion.getVersion()); logger.info(msg); getServletContext().log(msg); } }