/* * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * This library 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. See the GNU * Lesser General Public License for more details. * * Contributors: * Bogdan Stefanescu * Florent Guillaume */ package org.nuxeo.ecm.webengine.app; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; 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.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.web.common.ServletHelper; import org.nuxeo.ecm.platform.web.common.requestcontroller.filter.BufferingHttpServletResponse; import org.nuxeo.ecm.webengine.PathDescriptor; import org.nuxeo.ecm.webengine.WebEngine; import org.nuxeo.ecm.webengine.model.WebContext; import org.nuxeo.ecm.webengine.model.impl.AbstractWebContext; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.transaction.TransactionHelper; /** * This filter must be declared after the nuxeo authentication filter since it * needs an authentication info. * * The session synchronization is done only if NuxeoRequestControllerFilter was * not already done it and stateful flag for the request path is true. */ public class WebEngineFilter implements Filter { protected WebEngine engine; protected boolean isAutoTxEnabled; protected boolean isStatefull; protected static Log log = LogFactory.getLog(WebEngineFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { initIfNeeded(); } protected void initIfNeeded() { if (engine != null || Framework.getRuntime() == null) { return; } engine = Framework.getLocalService(WebEngine.class); } @Override public void destroy() { engine = null; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { initIfNeeded(); if (request instanceof HttpServletRequest) { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; PathDescriptor pd = engine.getRequestConfiguration().getMatchingConfiguration( req); Config config = new Config(req, pd); AbstractWebContext ctx = initRequest(config, req, resp); if (config.txStarted) { resp = new BufferingHttpServletResponse(resp); } try { preRequest(req, resp); chain.doFilter(request, resp); postRequest(req, resp); } catch (Throwable e) { TransactionHelper.setTransactionRollbackOnly(); if (e instanceof ServletException) { throw (ServletException) e; } else if (e instanceof IOException) { throw (IOException) e; } else { throw new ServletException(e); } } finally { try { cleanup(config, ctx, req, resp); } finally { if (config.txStarted) { ((BufferingHttpServletResponse) resp).stopBuffering(); } } } } else { chain.doFilter(request, response); } } public void preRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // need to set the encoding of characters manually if (null == request.getCharacterEncoding()) { request.setCharacterEncoding("UTF-8"); } // response.setCharacterEncoding("UTF-8"); } public void postRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // check if the target resource don't want automatic headers to be // inserted if (null != request.getAttribute("org.nuxeo.webengine.DisableAutoHeaders")) { // insert automatic headers response.addHeader("Pragma", "no-cache"); response.addHeader("Cache-Control", "no-cache"); response.addHeader("Cache-Control", "no-store"); response.addHeader("Cache-Control", "must-revalidate"); response.addHeader("Expires", "0"); response.setDateHeader("Expires", 0); // prevents caching } } public AbstractWebContext initRequest(Config config, HttpServletRequest request, HttpServletResponse response) { initTx(config, request); // user session is registered even for static resources - because some // static resources are served by JAX-RS resources that needs a user // session DefaultContext ctx = new DefaultContext((HttpServletRequest) request); request.setAttribute(WebContext.class.getName(), ctx); return ctx; } public void cleanup(Config config, AbstractWebContext ctx, HttpServletRequest request, HttpServletResponse response) { try { closeTx(config, request); } finally { request.removeAttribute(WebContext.class.getName()); } } public void initTx(Config config, HttpServletRequest req) { if (!config.isStatic && config.autoTx && !TransactionHelper.isTransactionActive()) { config.txStarted = ServletHelper.startTransaction(req); } } public void closeTx(Config config, HttpServletRequest req) { if (config.txStarted) { TransactionHelper.commitOrRollbackTransaction(); } } protected static class Config { boolean autoTx; boolean txStarted; boolean locked; boolean isStatic; String pathInfo; public Config(HttpServletRequest req, PathDescriptor pd) { autoTx = pd == null ? true : pd.isAutoTx(true); pathInfo = req.getPathInfo(); if (pathInfo == null || pathInfo.length() == 0) { pathInfo = "/"; } String spath = req.getServletPath(); isStatic = spath.contains("/skin") || pathInfo.contains("/skin/"); } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("WebEngineFilter&Confi:"); sb.append("\nPath Info:"); sb.append(pathInfo); sb.append("\nAuto TX:"); sb.append(autoTx); sb.append("\nStatic:"); sb.append(isStatic); return sb.toString(); } } }