001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.main;
029
030import java.io.IOException;
031
032import javax.servlet.Filter;
033import javax.servlet.FilterChain;
034import javax.servlet.FilterConfig;
035import javax.servlet.ServletException;
036import javax.servlet.ServletRequest;
037import javax.servlet.ServletResponse;
038import javax.servlet.http.HttpServletRequest;
039
040import org.apache.commons.logging.Log;
041
042/**
043 * Implements a servlet filter for URL rewriting.
044 * It adds the servlet name (typically "/opencms") if not already present, but necessary.
045 */
046public class OpenCmsUrlServletFilter implements Filter {
047
048    /** The static log object for this class. */
049    static final Log LOG = CmsLog.getLog(OpenCmsUrlServletFilter.class);
050
051    /**
052     * Name of the init-param of the filter configuration that is used to provide
053     * a pipe separated list of additional prefixes for which the URIs should not be adjusted.
054     */
055    private static final String INIT_PARAM_ADDITIONAL_EXCLUDEPREFIXES = "additionalExcludePrefixes";
056
057    /** A pipe separated list of URI prefixes, for which URIs should not be adjusted, additionally to the default prefixes. */
058    private String m_additionalExcludePrefixes;
059
060    /** The servlet context. */
061    private String m_contextPath;
062    /** Flag, indicating if the filter has been initialized. */
063    private boolean m_isInitialized;
064
065    /**
066     * A regex that matches if the requested URI starts with one of the default exclude prefixes
067     * or a prefix listed in the value of the {@link #INIT_PARAM_ADDITIONAL_EXCLUDEPREFIXES} init-param.
068     */
069    private String m_regex;
070
071    /** The servlet name, prefixed by "/". */
072    private String m_servletPath;
073
074    /**
075     * Creates a regex that matches all URIs starting with one of the <code>defaultExcludePrefixes</code>
076     * or one of the <code>additionalExcludePrefixes</code>.
077     *
078     * @param contextPath The context path that every URI starts with.
079     * @param defaultExcludePrefixes the default exclude prefixes.
080     * @param additionalExcludePrefixes a pipe separated list of URI prefixes for which the URLs
081     *  should not be adjusted - additionally to the default exclude prefixes
082     *
083     * @return a regex that matches all URIs starting with one of the <code>defaultExcludePrefixes</code>
084     *  or one of the <code>additionalExcludePrefixes</code>.
085     */
086    static String createRegex(String contextPath, String[] defaultExcludePrefixes, String additionalExcludePrefixes) {
087
088        StringBuffer regex = new StringBuffer();
089        regex.append(contextPath);
090        regex.append('(');
091        regex.append(defaultExcludePrefixes[0]);
092        for (int i = 1; i < defaultExcludePrefixes.length; i++) {
093            regex.append('|').append(defaultExcludePrefixes[i]);
094        }
095        if (!((null == additionalExcludePrefixes) || additionalExcludePrefixes.isEmpty())) {
096            regex.append('|').append(additionalExcludePrefixes);
097        }
098        regex.append(')');
099        regex.append(".*");
100        return regex.toString();
101    }
102
103    /**
104     * @see javax.servlet.Filter#destroy()
105     */
106    public void destroy() {
107
108        m_additionalExcludePrefixes = null;
109
110    }
111
112    /**
113     * Adjusts the requested URIs by prepending the name of the {@link org.opencms.main.OpenCmsServlet},
114     * if the request should be handled by that servlet.
115     *
116     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
117     */
118    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
119    throws IOException, ServletException {
120
121        if (m_isInitialized || tryToInitialize()) {
122            if (request instanceof HttpServletRequest) {
123                HttpServletRequest req = (HttpServletRequest)request;
124                String uri = req.getRequestURI();
125                if (!uri.matches(m_regex)) {
126                    String adjustedUri = uri.replaceFirst(m_contextPath + "/", m_servletPath);
127                    req.getRequestDispatcher(adjustedUri).forward(request, response);
128                    return;
129                }
130            }
131        }
132        chain.doFilter(request, response);
133    }
134
135    /**
136     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
137     */
138    public void init(FilterConfig filterConfig) {
139
140        m_additionalExcludePrefixes = filterConfig.getInitParameter(INIT_PARAM_ADDITIONAL_EXCLUDEPREFIXES);
141    }
142
143    /**
144     * Checks if OpenCms has reached run level 4 and if so, it initializes the member variables.
145     * In particular {@link #m_regex}, {@link #m_contextPath} and {@link OpenCmsUrlServletFilter#m_servletPath} are initialized and sets {@link #m_isInitialized} to true.
146     *
147     * @return <code>true</code> if initialization was successful, <code>false</code> otherwise.
148     */
149    boolean tryToInitialize() {
150
151        if (m_isInitialized) {
152            return true;
153        }
154        if (OpenCms.getRunLevel() == 4) {
155            m_contextPath = OpenCms.getSystemInfo().getContextPath();
156            m_servletPath = OpenCms.getSystemInfo().getServletPath() + "/";
157
158            /**
159             * The URI prefixes, for which the requested URI should not be rewritten,
160             * i.e., the ones that should not be handled by the {@link org.opencms.main.OpenCmsServlet}.
161             */
162            String[] defaultExcludePrefixes = new String[] {
163                "/" + OpenCms.getStaticExportManager().getExportPathForConfiguration(),
164                "/workplace",
165                "/VAADIN/",
166                m_servletPath,
167                "/resources/",
168                "/webdav",
169                "/cmisatom",
170                "/handle404",
171                "/services"};
172            StringBuffer regex = new StringBuffer();
173            regex.append(m_contextPath);
174            regex.append('(');
175            regex.append(defaultExcludePrefixes[0]);
176            for (int i = 1; i < defaultExcludePrefixes.length; i++) {
177                regex.append('|').append(defaultExcludePrefixes[i]);
178            }
179            if (!((null == m_additionalExcludePrefixes) || m_additionalExcludePrefixes.isEmpty())) {
180                regex.append('|').append(m_additionalExcludePrefixes);
181            }
182            regex.append(')');
183            regex.append(".*");
184            m_regex = regex.toString();
185            m_isInitialized = true;
186            return true;
187        }
188        return false;
189    }
190}