001/*
002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.ecm.platform.ui.web.rest;
020
021import java.io.IOException;
022
023import javax.servlet.Filter;
024import javax.servlet.FilterChain;
025import javax.servlet.FilterConfig;
026import javax.servlet.RequestDispatcher;
027import javax.servlet.ServletContext;
028import javax.servlet.ServletException;
029import javax.servlet.ServletRequest;
030import javax.servlet.ServletResponse;
031import javax.servlet.http.HttpServletRequest;
032import javax.servlet.http.HttpServletResponse;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.nuxeo.ecm.core.io.download.DownloadHelper;
037import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService;
038import org.nuxeo.ecm.platform.url.api.DocumentView;
039import org.nuxeo.runtime.api.Framework;
040
041/**
042 * Filter used to decode URLs and wrap requests to enable encoding. This filter is useful because Nuxeo support
043 * pluggable URL patterns
044 *
045 * @author tiry
046 */
047public class FancyURLFilter implements Filter {
048
049    private static final Log log = LogFactory.getLog(FancyURLFilter.class);
050
051    protected URLPolicyService urlService;
052
053    protected ServletContext servletContext;
054
055    @Override
056    public void init(FilterConfig conf) throws ServletException {
057        log.debug("Nuxeo5 URLFilter started");
058        servletContext = conf.getServletContext();
059    }
060
061    protected URLPolicyService getUrlService() {
062        if (urlService == null) {
063            urlService = Framework.getService(URLPolicyService.class);
064        }
065        return urlService;
066    }
067
068    @Override
069    public void destroy() {
070        urlService = null;
071    }
072
073    @Override
074    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
075            ServletException {
076        HttpServletRequest httpRequest = (HttpServletRequest) request;
077        HttpServletResponse httpResponse = (HttpServletResponse) response;
078        try {
079            getUrlService();
080            // initialize its view id manager if necessary
081            urlService.initViewIdManager(servletContext, httpRequest, httpResponse);
082
083            // check if this is an URL that needs to be parsed
084            if (urlService.isCandidateForDecoding(httpRequest)) {
085                DocumentView docView = urlService.getDocumentViewFromRequest(httpRequest);
086                // if parse succeeded => process
087                if (docView != null) {
088                    // put it in request
089                    urlService.setDocumentViewInRequest(httpRequest, docView);
090
091                    // get the view id for navigation from the stored outcome
092                    String jsfOutcome = docView.getViewId();
093
094                    // get target page according to navigation rules
095                    String target = urlService.getViewIdFromOutcome(jsfOutcome, httpRequest);
096
097                    // dispatch
098                    RequestDispatcher dispatcher;
099                    if (target != null) {
100                        dispatcher = httpRequest.getRequestDispatcher(target);
101                    } else {
102                        // Use a dummy dispatcher if the target is not needed.
103                        // This comes handy for instance for nxfile url
104                        dispatcher = httpRequest.getRequestDispatcher("/malformed_url_error_page.faces");
105                    }
106                    // set force encoding in case forward triggers a
107                    // redirect (when a seam page is processed for instance).
108                    request.setAttribute(URLPolicyService.FORCE_URL_ENCODING_REQUEST_KEY, Boolean.TRUE);
109                    // forward request to the target viewId
110                    dispatcher.forward(new FancyURLRequestWrapper(httpRequest, docView),
111                            wrapResponse(httpRequest, httpResponse));
112                    return;
113                }
114            }
115
116            // do not filter if it's candidate for encoding so soon : document
117            // view has not been set in the request yet => always wrap
118            chain.doFilter(request, wrapResponse(httpRequest, httpResponse));
119
120        } catch (IOException e) {
121            String url = httpRequest.getRequestURL().toString();
122            if (DownloadHelper.isClientAbortError(e)) {
123                DownloadHelper.logClientAbort(e);
124                log.debug(String.format("Client disconnected from URL %s : %s", url, e.getMessage()));
125            } else {
126                throw new IOException("On requestURL: " + url, e);
127            }
128        } catch (ServletException e) {
129            String url = httpRequest.getRequestURL().toString();
130            if (DownloadHelper.isClientAbortError(e)) {
131                DownloadHelper.logClientAbort(e);
132                log.debug(String.format("Client disconnected from URL %s : %s", url, e.getMessage()));
133            } else {
134                throw new ServletException("On requestURL: " + url, e);
135            }
136        }
137
138    }
139
140    protected ServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
141        return new FancyURLResponseWrapper(response, request);
142    }
143
144}