001/*
002* File   : $Source$
003* Date   : $Date$
004* Version: $Revision$
005*
006* This library is part of OpenCms -
007* the Open Source Content Management System
008*
009* Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com)
010*
011* This library is free software; you can redistribute it and/or
012* modify it under the terms of the GNU Lesser General Public
013* License as published by the Free Software Foundation; either
014* version 2.1 of the License, or (at your option) any later version.
015*
016* This library is distributed in the hope that it will be useful,
017* but WITHOUT ANY WARRANTY; without even the implied warranty of
018* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019* Lesser General Public License for more details.
020*
021* For further information about Alkacon Software, please see the
022* company website: http://www.alkacon.com
023*
024* For further information about OpenCms, please see the
025* project website: http://www.opencms.org
026*
027* You should have received a copy of the GNU Lesser General Public
028* License along with this library; if not, write to the Free Software
029* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
030*/
031
032package org.opencms.main;
033
034import org.opencms.configuration.CmsParameterConfiguration;
035import org.opencms.file.CmsObject;
036import org.opencms.search.CmsSearchException;
037import org.opencms.search.CmsSearchManager;
038import org.opencms.search.solr.CmsSolrIndex;
039import org.opencms.search.solr.CmsSolrQuery;
040import org.opencms.site.CmsSite;
041import org.opencms.util.CmsRequestUtil;
042import org.opencms.util.CmsStringUtil;
043
044import java.io.IOException;
045import java.util.Arrays;
046import java.util.Collection;
047import java.util.Map;
048
049import javax.servlet.http.HttpServlet;
050import javax.servlet.http.HttpServletRequest;
051import javax.servlet.http.HttpServletResponse;
052
053import org.apache.commons.logging.Log;
054import org.apache.solr.common.params.CommonParams;
055
056/**
057 * The OpenCms Solr handler.<p>
058 *
059 * Reachable under: "/opencms/opencms/handleSolrSelect".<p>
060 *
061 * Usage example:<p>
062 * <code>http://localhost:8080/opencms/opencms/handleSolrSelect?fq=parent-folders:/sites/+type=v8article&fl=path&rows=10&sort=path%20asc</code>
063 *
064 * @since 8.5.0
065 */
066public class OpenCmsSolrHandler extends HttpServlet implements I_CmsRequestHandler {
067
068    /**
069     * Encapsulate each request with an inner class in order to make OpenCmsSolrHander thread-safe.
070     */
071    class Context {
072
073        /** The CMS object. */
074        public CmsObject m_cms;
075
076        /** The Solr index. */
077        public CmsSolrIndex m_index;
078
079        /** The request parameters. */
080        public Map<String, String[]> m_params;
081
082        /** The Solr query. */
083        public CmsSolrQuery m_query;
084
085    }
086
087    /**
088     * An enum storing the handler names implemented by this class.<p>
089     */
090    private static enum HANDLER_NAMES {
091
092        /**
093         * A constant for the '/select' request handler of the embedded Solr server.
094         * This handler is reachable under "/opencms/opencms/handleSolrSelect".<p>
095         */
096        SolrSelect,
097
098        /**
099         * A constant for the '/spell' request handler of the embedded Solr server.
100         * This handler is reachable under "/opencms/opencms/handleSolrSpell".<p>
101         */
102        SolrSpell
103    }
104
105    /** The log object for this class. */
106    private static final Log LOG = CmsLog.getLog(OpenCmsSolrHandler.class);
107
108    /** A constant for the optional 'baseUri' parameter. */
109    public static final String PARAM_BASE_URI = "baseUri";
110
111    /** A constant for the optional 'core' parameter. */
112    public static final String PARAM_CORE = "core";
113
114    /** A constant for the optional 'index' parameter. */
115    public static final String PARAM_INDEX = "index";
116
117    /** A constant for the HTTP 'referer'. */
118    protected static final String HEADER_REFERER_KEY = "referer";
119
120    /** The UID. */
121    private static final long serialVersionUID = 2460644631508735724L;
122
123    /** The default allowed wt parameter values  */
124    public static final Collection<String> DEFAULT_ALLOWED_WRITE_TO_VALUES = Arrays.asList(
125        new String[] {"json", "xml"});
126
127    /** Config parameter for the request handler to set the allowed "wt" parameter values. */
128    private static final String CONFIG_PARAM_ALLOWED_WRITE_TO = "allowedWriteTo";
129
130    /** The allowed wt parameter values */
131    public Collection<String> m_allowedWriteTo;
132
133    /**
134     * OpenCms servlet main request handling method.<p>
135     *
136     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
137     */
138    @Override
139    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
140
141        handle(req, res, HANDLER_NAMES.SolrSelect.toString());
142    }
143
144    /**
145     * OpenCms servlet POST request handling method,
146     * will just call {@link #doGet(HttpServletRequest, HttpServletResponse)}.<p>
147     *
148     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
149     */
150    @Override
151    public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
152
153        doGet(req, res);
154    }
155
156    /**
157     * @see org.opencms.main.I_CmsRequestHandler#getHandlerNames()
158     */
159    public String[] getHandlerNames() {
160
161        return CmsStringUtil.enumNameToStringArray(HANDLER_NAMES.values());
162    }
163
164    /**
165     * @see org.opencms.main.I_CmsRequestHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
166     */
167    public void handle(HttpServletRequest req, HttpServletResponse res, String name) throws IOException {
168
169        final HANDLER_NAMES handlerName = HANDLER_NAMES.valueOf(name);
170        if (handlerName != null) {
171            try {
172                Context context = initializeRequest(req, res);
173                if ((context.m_params.get(CommonParams.Q) != null) || (context.m_params.get(CommonParams.FQ) != null)) {
174                    switch (handlerName) {
175                        case SolrSelect:
176                            context.m_index.select(res, context.m_cms, context.m_query, true);
177                            break;
178                        case SolrSpell:
179                            context.m_index.spellCheck(res, context.m_cms, context.m_query);
180                            break;
181                        default:
182                            break;
183                    }
184                }
185            } catch (Exception e) {
186                if (LOG.isInfoEnabled()) {
187                    LOG.info(e.getLocalizedMessage(), e);
188                }
189                res.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
190            }
191        }
192    }
193
194    /**
195     * @see org.opencms.main.I_CmsRequestHandler#initParameters(org.opencms.configuration.CmsParameterConfiguration)
196     */
197    @Override
198    public void initParameters(CmsParameterConfiguration params) {
199
200        String allowedWriteTo = params.getString(CONFIG_PARAM_ALLOWED_WRITE_TO, null);
201        m_allowedWriteTo = null != allowedWriteTo
202        ? Arrays.asList(allowedWriteTo.split(","))
203        : DEFAULT_ALLOWED_WRITE_TO_VALUES;
204    }
205
206    /**
207     * Returns the CMS object.<p>
208     *
209     * @param req the request
210     *
211     * @return the CMS object
212     *
213     * @throws CmsException if something goes wrong
214     */
215    protected CmsObject getCmsObject(HttpServletRequest req) throws CmsException {
216
217        CmsObject cms = OpenCmsCore.getInstance().initCmsObjectFromSession(req);
218        // use the guest user as fall back
219        if (cms == null) {
220            cms = OpenCmsCore.getInstance().initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
221            String siteRoot = OpenCmsCore.getInstance().getSiteManager().matchRequest(req).getSiteRoot();
222            cms.getRequestContext().setSiteRoot(siteRoot);
223        }
224        String baseUri = getBaseUri(req, cms);
225        if (baseUri != null) {
226            cms.getRequestContext().setUri(baseUri);
227        }
228        return cms;
229    }
230
231    /**
232     * Initialized the search request and sets the local parameter.<p>
233     *
234     * @param req the servlet request
235     * @param res the servlet response
236     *
237     * @return the generated context
238     *
239     * @throws CmsException if something goes wrong
240     * @throws Exception if something goes wrong
241     * @throws CmsSearchException if something goes wrong
242     * @throws IOException if something goes wrong
243     */
244    protected Context initializeRequest(HttpServletRequest req, HttpServletResponse res)
245    throws CmsException, Exception, CmsSearchException, IOException {
246
247        Context context = new Context();
248        context.m_cms = getCmsObject(req);
249        context.m_params = CmsRequestUtil.createParameterMap(req.getParameterMap());
250        String[] wtValues = context.m_params.get(CommonParams.WT);
251        if ((null != wtValues) && (wtValues.length > 0)) {
252            String origWtValue = wtValues[0];
253            if (wtValues.length > 1) {
254                context.m_params.put(CommonParams.WT, new String[] {origWtValue});
255                if (LOG.isDebugEnabled()) {
256                    LOG.debug(
257                        "Called Solr handler with multiple 'wt' params. Keeping only the value '" + origWtValue + "'.");
258                }
259            }
260            if (!m_allowedWriteTo.contains(origWtValue)) {
261                if (LOG.isDebugEnabled()) {
262                    LOG.debug(
263                        "Called Solr handler with forbidden 'wt' parameter value '"
264                            + origWtValue
265                            + "'. The value is removed.");
266                }
267                context.m_params.remove(CommonParams.WT);
268            }
269        }
270        String[] qtValues = context.m_params.get(CommonParams.QT);
271        if ((null != qtValues) && (qtValues.length > 0)) {
272            String origQtValue = qtValues[0];
273            if (qtValues.length > 1) {
274                context.m_params.put(CommonParams.QT, new String[] {origQtValue});
275                if (LOG.isDebugEnabled()) {
276                    LOG.debug(
277                        "Called Solr handler with multiple 'qt' params. Keeping only the value '" + origQtValue + "'.");
278                }
279            }
280            if ((origQtValue != null) && origQtValue.startsWith("/")) {
281                if (LOG.isDebugEnabled()) {
282                    LOG.debug(
283                        "Called Solr handler with forbidden 'qt' parameter value '"
284                            + origQtValue
285                            + "'. The value is removed.");
286                }
287                context.m_params.remove(CommonParams.QT);
288            }
289        }
290        context.m_index = CmsSearchManager.getIndexSolr(context.m_cms, context.m_params);
291
292        if (context.m_index != null) {
293            context.m_query = new CmsSolrQuery(context.m_cms, context.m_params);
294        } else {
295            res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
296            if (LOG.isInfoEnabled()) {
297                String indexName = context.m_params.get(PARAM_CORE) != null
298                ? context.m_params.get(PARAM_CORE)[0]
299                : (context.m_params.get(PARAM_INDEX) != null ? context.m_params.get(PARAM_INDEX)[0] : null);
300                LOG.info(Messages.get().getBundle().key(Messages.GUI_SOLR_INDEX_NOT_FOUND_1, indexName));
301            }
302        }
303
304        return context;
305    }
306
307    /**
308     * Returns the base URI.<p>
309     *
310     * @param req the servlet request
311     * @param cms the CmsObject
312     *
313     * @return the base URI
314     */
315    private String getBaseUri(HttpServletRequest req, CmsObject cms) {
316
317        String baseUri = req.getParameter(PARAM_BASE_URI);
318        if (CmsStringUtil.isEmptyOrWhitespaceOnly(baseUri)) {
319            String referer = req.getHeader(HEADER_REFERER_KEY);
320            CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(cms.getRequestContext().getSiteRoot());
321            if (site != null) {
322                String prefix = site.getServerPrefix(cms, "/") + OpenCms.getStaticExportManager().getVfsPrefix();
323                if ((referer != null) && referer.startsWith(prefix)) {
324                    baseUri = referer.substring(prefix.length());
325                }
326            }
327        }
328        return baseUri;
329    }
330}