package org.nutz.mvc; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.nutz.conf.NutConf; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.util.Context; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.mvc.config.FilterNutConfig; /** * 同 JSP/Serlvet 容器的挂接点 * * @author zozoh(zozohtnt@gmail.com) * @author juqkai(juqkai@gmail.com) * @author wendal(wendal1985@gmail.com) */ public class NutFilter implements Filter { protected static Log log; protected ActionHandler handler; protected static final String IGNORE = "^.+\\.(jsp|png|gif|jpg|js|css|jspx|jpeg|swf|ico|map)$"; protected Pattern ignorePtn; protected String selfName; protected SessionProvider sp; private NutFilter2 proxyFilter;//代理老版本的Filter /** * 需要排除的路径前缀 */ protected Pattern exclusionsPrefix; /** * 需要排除的后缀名 */ protected Pattern exclusionsSuffix; /** * 需要排除的固定路径 */ protected Set<String> exclusionPaths; protected ServletContext sc; public void init(FilterConfig conf) throws ServletException { try { if ("disable".equals(conf.getInitParameter("fast-class"))) { NutConf.USE_FASTCLASS = false; } _init(conf); } finally { Mvcs.set(null, null, null); Mvcs.ctx().removeReqCtx(); } } public void _init(FilterConfig conf) throws ServletException { log = Logs.getLog(getClass()); sc = conf.getServletContext(); Mvcs.setServletContext(sc); if ("true".equals(Strings.sNull(conf.getInitParameter("skip-mode"), "false").toLowerCase())) { log.infof("NutFilter[%s] run as skip-mode", conf.getFilterName()); proxyFilter = new NutFilter2(); return; } log.infof("NutFilter[%s] starting ...", conf.getFilterName()); this.selfName = conf.getFilterName(); Mvcs.set(selfName, null, null); FilterNutConfig config = new FilterNutConfig(conf); Mvcs.setNutConfig(config); handler = new ActionHandler(config); String regx = Strings.sNull(config.getInitParameter("ignore"), IGNORE); if (!"null".equalsIgnoreCase(regx)) { ignorePtn = Pattern.compile(regx, Pattern.CASE_INSENSITIVE); } String exclusions = config.getInitParameter("exclusions"); if (exclusions != null) { String[] tmps = Strings.splitIgnoreBlank(exclusions); Set<String> prefix = new HashSet<String>(); Set<String> suffix = new HashSet<String>(); Set<String> paths = new HashSet<String>(); for (String tmp : tmps) { tmp = tmp.trim().intern(); if (tmp.length() > 1) { if (tmp.startsWith("*")) { suffix.add(tmp.substring(1)); continue; } else if (tmp.endsWith("*")) { prefix.add(tmp.substring(0, tmp.length() - 1)); continue; } } paths.add(tmp); } if (prefix.size() > 0) { exclusionsPrefix = Pattern.compile("^("+Lang.concat("|", prefix)+")", Pattern.CASE_INSENSITIVE); log.info("exclusionsPrefix = " + exclusionsPrefix); } if (suffix.size() > 0) { exclusionsSuffix = Pattern.compile("("+Lang.concat("|", suffix)+")$", Pattern.CASE_INSENSITIVE); log.info("exclusionsSuffix = " + exclusionsSuffix); } if (paths.size() > 0) { exclusionPaths = paths; log.info("exclusionsPath = " + exclusionPaths); } } sp = config.getSessionProvider(); } public void destroy() { if (proxyFilter != null) return; Mvcs.resetALL(); Mvcs.set(selfName, null, null); if (handler != null) handler.depose(); Mvcs.close(); Mvcs.setServletContext(null); Mvcs.set(null, null, null); Mvcs.ctx().removeReqCtx(); } /** * 过滤请求. 过滤顺序(ignorePtn,exclusionsSuffix,exclusionsPrefix,exclusionPaths) * @param matchUrl 待匹配URL * @return 需要排除则返回true * @throws IOException 不太可能抛出 * @throws ServletException 不太可能抛出 */ protected boolean isExclusion(String matchUrl) throws IOException, ServletException { if (ignorePtn != null && ignorePtn.matcher(matchUrl).find()) { return true; } if (exclusionsSuffix != null) { if (exclusionsSuffix.matcher(matchUrl).find()) { return true; } } if (exclusionsPrefix != null) { if (exclusionsPrefix.matcher(matchUrl).find()) { return true; } } if (exclusionPaths != null && exclusionPaths.contains(matchUrl)) { return true; } return false; } public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain chain) throws IOException, ServletException { if (!Mvcs.DISABLE_X_POWERED_BY) ((HttpServletResponse)resp).setHeader("X-Powered-By", Mvcs.X_POWERED_BY); ServletContext prCtx = Mvcs.getServletContext(); Mvcs.setServletContext(sc); if (proxyFilter != null) { proxyFilter.doFilter(req, resp, chain); return; } HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; String matchUrl = request.getServletPath() + Strings.sBlank(request.getPathInfo()); String markKey = "nutz_ctx_mark"; Integer mark = (Integer) req.getAttribute(markKey); if (mark != null) { req.setAttribute(markKey, mark+1); } else { req.setAttribute(markKey, 0); } String preName = Mvcs.getName(); Context preContext = Mvcs.resetALL(); try { if (sp != null) request = sp.filter(request, response, Mvcs.getServletContext()); Mvcs.set(this.selfName, request, response); if (!isExclusion(matchUrl)) { if (handler.handle(request, response)) return; } nextChain(request, response, chain); } finally { //仅当forward/incule时,才需要恢复之前设置 if (mark != null) { Mvcs.ctx().reqCtx(preContext); Mvcs.setServletContext(prCtx); Mvcs.set(preName, request, response); if (mark == 0) { req.removeAttribute(markKey); } else { req.setAttribute(markKey, mark - 1); } } else { Mvcs.set(null, null, null); Mvcs.ctx().removeReqCtx(); Mvcs.setServletContext(null); } } } protected void nextChain(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException { // 更新 Request 必要的属性 Mvcs.updateRequestAttributes((HttpServletRequest) req); // 本过滤器没有找到入口函数,继续其他的过滤器 chain.doFilter(req, resp); } }