/*
* (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.nuxeo.ecm.webengine.app;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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.lang3.StringUtils;
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.WebEngine;
import org.nuxeo.ecm.webengine.model.WebContext;
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 {
engine = Framework.getLocalService(WebEngine.class);
}
@Override
public void destroy() {
engine = null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
new UnitOfWork((HttpServletRequest) request, (HttpServletResponse)response).doFilter(chain);
}
private static class UnitOfWork {
private final boolean txStarted;
private final boolean isStatic;
private final String pathInfo;
private final DefaultContext context;
private UnitOfWork(HttpServletRequest req, HttpServletResponse resp) throws IOException {
pathInfo = StringUtils.isEmpty(req.getPathInfo()) ? "/" : req.getPathInfo();
isStatic = req.getServletPath().contains("/skin") || pathInfo.contains("/skin/");
txStarted = !isStatic && !TransactionHelper.isTransactionActive()
&& ServletHelper.startTransaction(req);
context = new DefaultContext(req, txStarted ? new BufferingHttpServletResponse(resp) : resp);
req.setAttribute(WebContext.class.getName(), context);
}
private void doFilter(FilterChain chain) throws ServletException, IOException {
boolean completedAbruptly = true;
try {
preRequest();
chain.doFilter(context.getRequest(), context.getResponse());
postRequest();
completedAbruptly = false;
} catch (IOException | ServletException | RuntimeException error) {
context.getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
error.getMessage());
throw error;
} finally {
cleanup(completedAbruptly);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("WebEngine Filter:");
sb.append("\nPath Info:");
sb.append(pathInfo);
sb.append("\nStatic:");
sb.append(isStatic);
return sb.toString();
}
void cleanup(boolean completedAbruptly) throws IOException {
context.getRequest().removeAttribute(WebContext.class.getName());
if (!txStarted) {
return;
}
if (completedAbruptly) {
TransactionHelper.setTransactionRollbackOnly();
}
try {
TransactionHelper.commitOrRollbackTransaction();
} catch (RuntimeException cause) {
context.getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, cause.getMessage());
} finally {
((BufferingHttpServletResponse) context.getResponse()).stopBuffering();
}
}
void preRequest() {
// need to set the encoding of characters manually
HttpServletRequest request = context.getRequest();
if (request.getCharacterEncoding() == null) {
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
void postRequest() {
HttpServletRequest request = context.getRequest();
HttpServletResponse response = context.getResponse();
// 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
}
}
}
}