/* * Copyright 2007-2010 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 is 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 net.paoding.rose.web.impl.thread; import java.lang.reflect.InvocationTargetException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import net.paoding.rose.RoseConstants; import net.paoding.rose.util.SpringUtils; import net.paoding.rose.util.StackTraceSimplifier; import net.paoding.rose.web.ControllerErrorHandler; import net.paoding.rose.web.Invocation; import net.paoding.rose.web.annotation.MultipartCleanup; import net.paoding.rose.web.annotation.NotForSubModules; import net.paoding.rose.web.annotation.SuppressMultipartResolver; import net.paoding.rose.web.impl.module.Module; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.util.WebUtils; /** * {@link ModuleEngine} 负责从表示的模块中找出可匹配的 控制器引擎 {@link ControllerEngine} * 并委托其返回匹配的 {@link ActionEngine}对象,最终构成 {@link InvocationBean}对象返回. * <p> * {@link ModuleEngine}能够从失败控制器引擎匹配中走出来,继续匹配下一个控制器引擎,找到最终的 * {@link ActionEngine}对象。即,如果一个匹配的控制器引擎中没有匹配的 {@link ActionEngine}对象, * {@link ModuleEngine}能够自动到下一个匹配的{@link ControllerEngine}对象去判断. * <p> * * @author 王志亮 [qieqie.wang@gmail.com] * @author Li Weibo[weibo.leo@gmail.com] */ public class ModuleEngine implements Engine { /** 日志对象 */ private static Log logger = LogFactory.getLog(ModuleEngine.class); /** 模块对象 */ private final Module module; private final MultipartResolver multipartResolver; // --------------------------------------------------------------------- /** * 构造能够正确匹配出到所给模块请求的控制器和方法的引擎,返回到相应 {@link InvocationBean}对象的模块引擎. * * @param module * @throws NullPointerException 如果所传入的模块为空时 */ public ModuleEngine(Module module) { if (module == null) { throw new NullPointerException("module"); } this.module = module; this.multipartResolver = initMultipartResolver(module.getApplicationContext()); } /** * 由构造子调用,创建给定模块对象的控制器引擎对象 * * @param module * @return */ // --------------------------------------------------------------------- /** * 返回所包含模块对象 * * @return */ public Module getModule() { return module; } public MultipartResolver getMultipartResolver() { return multipartResolver; } @Override public int isAccepted(HttpServletRequest rose) { return 1; } @Override public Object execute(Rose rose) throws Throwable { Invocation inv = rose.getInvocation(); // 按照Spring规范,设置当前的applicationContext对象到request对象中,用于messageSource/国际化等功能 inv.getRequest().setAttribute(RoseConstants.WEB_APPLICATION_CONTEXT_ATTRIBUTE, module.getApplicationContext()); boolean isMultiPartRequest = false; try { isMultiPartRequest = checkMultipart(inv); return rose.doNext(); } catch (Throwable invException) { // 抛出异常了(可能是拦截器或控制器抛出的),此时让该控制器所在模块的ControllerErrorHanlder处理 Throwable cause = invException; // 因为使用的是类反射技术,所以需要先把实际异常从InvocationTargetException取出来 while (cause instanceof InvocationTargetException) { cause = ((InvocationTargetException) cause).getTargetException(); } // Module errorHandlerModule = module; ControllerErrorHandler errorHandler = errorHandlerModule.getErrorHandler(); while (errorHandler == null && errorHandlerModule != null) { errorHandlerModule = errorHandlerModule.getParent(); if (errorHandlerModule != null) { errorHandler = errorHandlerModule.getErrorHandler(); if (errorHandler != null) { if (errorHandler.getClass().isAnnotationPresent(NotForSubModules.class)) { errorHandler = null; continue; } } } else { errorHandler = null; break; } } Object instruction = null; if (errorHandler != null) { if (logger.isDebugEnabled()) { logger.debug("exception happended; " + errorHandler.getClass().getName() + " will handle the exception: " // + cause.getClass().getName() + ":" + cause.getMessage()); } rose.getInvocation().setViewModule(errorHandlerModule); // HttpServletRequest request = rose.getInvocation().getRequest(); WebUtils.exposeErrorRequestAttributes(request, cause, null); StackTraceSimplifier.simplify(cause); //对栈进行简化 instruction = errorHandler.onError(rose.getInvocation(), cause); } // onError方法返回null,表示需要重新throw出去 // rethrow出去的不是cause而是invException,目的要把整个异常抛出来,以让知道整个异常的来由 if ((errorHandler == null) || (instruction == null)) { if (invException instanceof Exception) { throw (Exception) invException; } else { throw (Error) invException; } } return instruction; } finally { if (isMultiPartRequest) { cleanupMultipart(inv); } } } public void destroy() { WebApplicationContext applicationContext = module.getApplicationContext(); if (applicationContext instanceof AbstractApplicationContext) { ((AbstractApplicationContext) applicationContext).close(); } } /** * 返回该module engine的映射地址 */ @Override public String toString() { //return this.multipartResolverodule.getMappingPath(); return this.module.getUrl().toString(); } //------------------------------------------------------ protected boolean checkMultipart(Invocation inv) throws MultipartException { if (inv.getRequest().getMethod() == null) { throw new NullPointerException("request.method"); } if (this.multipartResolver.isMultipart(inv.getRequest())) { if (inv.getRequest() instanceof MultipartHttpServletRequest) { logger.debug("Request is already a MultipartHttpServletRequest"); return true; } else { if (!inv.getMethod().isAnnotationPresent(SuppressMultipartResolver.class)) { inv.setRequest(this.multipartResolver.resolveMultipart(inv.getRequest())); return true; } } } return false; } /** * Clean up any resources used by the given multipart request (if any). * * @see MultipartResolver#cleanupMultipart */ protected void cleanupMultipart(Invocation inv) { HttpServletRequest src = inv.getRequest(); while (src != null && !(src instanceof MultipartHttpServletRequest) && src instanceof HttpServletRequestWrapper) { src = (HttpServletRequest) ((HttpServletRequestWrapper) src).getRequest(); } if (src instanceof MultipartHttpServletRequest) { final MultipartHttpServletRequest request = (MultipartHttpServletRequest) src; MultipartCleanup multipartCleaner = inv.getMethod().getAnnotation( MultipartCleanup.class); if (multipartCleaner == null || multipartCleaner.after() == MultipartCleanup.After.CONTROLLER_INVOCATION) { multipartResolver.cleanupMultipart(request); } else { inv.addAfterCompletion(new AfterCompletion() { @Override public void afterCompletion(Invocation inv, Throwable ex) throws Exception { ModuleEngine.this.multipartResolver.cleanupMultipart(request); } }); } } } private static MultipartResolver initMultipartResolver(ApplicationContext context) { MultipartResolver multipartResolver = (MultipartResolver) SpringUtils.getBean(context, MultipartResolver.class); if (multipartResolver != null) { if (logger.isDebugEnabled()) { logger.debug("Using MultipartResolver [" + multipartResolver + "]"); } } else { multipartResolver = new CommonsMultipartResolver(); if (logger.isDebugEnabled()) { logger.debug("No found MultipartResolver in context, " + "Using MultipartResolver by default [" + multipartResolver + "]"); } } return multipartResolver; } }