/* * OpenSearchServlet.java * * Version: $Revision: 1.20 $ * * Date: $Date: 2005/08/25 17:20:27 $ * * Copyright (c) 2002-2009, The DSpace Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the DSpace Foundation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.dspace.app.webui.servlet; import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.TransformerFactory; import javax.xml.transform.Transformer; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.TransformerException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.apache.log4j.Logger; import org.dspace.app.util.OpenSearch; import org.dspace.app.util.SyndicationFeed; import org.dspace.app.util.Util; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; import org.dspace.core.LogManager; import org.dspace.handle.HandleManager; import org.dspace.search.DSQuery; import org.dspace.search.QueryArgs; import org.dspace.search.QueryResults; import org.dspace.sort.SortOption; /** * Servlet for producing OpenSearch-compliant search results, and the * OpenSearch description document. * <p> * OpenSearch is a specification for describing and advertising search-engines * and their result formats. Commonly, RSS and Atom formats are used, which * the current implementation supports, as is HTML (used directly in browsers). * NB: this is baseline OpenSearch, no extensions currently supported. * </p> * <p> * The value of the "scope" parameter should be absent (which means no * scope restriction), or the handle of a community or collection, otherwise * parameters exactly match those of the SearchServlet. * </p> * * @author Richard Rodgers * */ public class OpenSearchServlet extends DSpaceServlet { private static final long serialVersionUID = 1L; private static String msgKey = "org.dspace.app.webui.servlet.FeedServlet"; /** log4j category */ private static Logger log = Logger.getLogger(OpenSearchServlet.class); // locale-sensitive metadata labels private Map<String, Map<String, String>> localeLabels = null; public void init() { localeLabels = new HashMap<String, Map<String, String>>(); } protected void doDSGet(Context context, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, SQLException, AuthorizeException { // dispense with simple service document requests String scope = request.getParameter("scope"); if (scope !=null && "".equals(scope)) { scope = null; } String path = request.getPathInfo(); if (path != null && path.endsWith("description.xml")) { String svcDescrip = OpenSearch.getDescription(scope); response.setContentType(OpenSearch.getContentType("opensearchdescription")); response.setContentLength(svcDescrip.length()); response.getWriter().write(svcDescrip); return; } // get enough request parameters to decide on action to take String format = request.getParameter("format"); if (format == null || "".equals(format)) { // default to atom format = "atom"; } // do some sanity checking if (! OpenSearch.getFormats().contains(format)) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } // then the rest - we are processing the query String query = request.getParameter("query"); int start = Util.getIntParameter(request, "start"); int rpp = Util.getIntParameter(request, "rpp"); int sort = Util.getIntParameter(request, "sort_by"); String order = request.getParameter("order"); String sortOrder = (order == null || order.length() == 0 || order.toLowerCase().startsWith("asc")) ? SortOption.ASCENDING : SortOption.DESCENDING; QueryArgs qArgs = new QueryArgs(); // can't start earlier than 0 in the results! if (start < 0) { start = 0; } qArgs.setStart(start); if (rpp > 0) { qArgs.setPageSize(rpp); } qArgs.setSortOrder(sortOrder); if (sort > 0) { try { qArgs.setSortOption(SortOption.getSortOption(sort)); } catch(Exception e) { // invalid sort id - do nothing } } qArgs.setSortOrder(sortOrder); // Ensure the query is non-null if (query == null) { query = ""; } // If there is a scope parameter, attempt to dereference it // failure will only result in its being ignored DSpaceObject container = (scope != null) ? HandleManager.resolveToObject(context, scope) : null; // Build log information String logInfo = ""; // get the start of the query results page qArgs.setQuery(query); // Perform the search QueryResults qResults = null; if (container == null) { qResults = DSQuery.doQuery(context, qArgs); } else if (container instanceof Collection) { logInfo = "collection_id=" + container.getID() + ","; qResults = DSQuery.doQuery(context, qArgs, (Collection)container); } else if (container instanceof Community) { logInfo = "community_id=" + container.getID() + ","; qResults = DSQuery.doQuery(context, qArgs, (Community)container); } // now instantiate the results DSpaceObject[] results = new DSpaceObject[qResults.getHitHandles().size()]; for (int i = 0; i < qResults.getHitHandles().size(); i++) { String myHandle = (String)qResults.getHitHandles().get(i); DSpaceObject dso = HandleManager.resolveToObject(context, myHandle); if (dso == null) { throw new SQLException("Query \"" + query + "\" returned unresolvable handle: " + myHandle); } results[i] = dso; } // Log log.info(LogManager.getHeader(context, "search", logInfo + "query=\"" + query + "\",results=(" + results.length + ")")); // format and return results Map<String, String> labelMap = getLabels(request); Document resultsDoc = OpenSearch.getResultsDoc(format, query, qResults, container, results, labelMap); try { Transformer xf = TransformerFactory.newInstance().newTransformer(); response.setContentType(OpenSearch.getContentType(format)); xf.transform(new DOMSource(resultsDoc), new StreamResult(response.getWriter())); } catch (TransformerException e) { log.error(e); throw new ServletException(e.toString()); } } private Map<String, String> getLabels(HttpServletRequest request) { // Get access to the localized resource bundle Locale locale = request.getLocale(); Map<String, String> labelMap = localeLabels.get(locale.toString()); if (labelMap == null) { labelMap = getLocaleLabels(locale); localeLabels.put(locale.toString(), labelMap); } return labelMap; } private Map<String, String> getLocaleLabels(Locale locale) { Map<String, String> labelMap = new HashMap<String, String>(); ResourceBundle labels = ResourceBundle.getBundle("Messages", locale); labelMap.put(SyndicationFeed.MSG_UNTITLED, labels.getString(msgKey + ".notitle")); labelMap.put(SyndicationFeed.MSG_LOGO_TITLE, labels.getString(msgKey + ".logo.title")); labelMap.put(SyndicationFeed.MSG_FEED_DESCRIPTION, labels.getString(msgKey + ".general-feed.description")); labelMap.put(SyndicationFeed.MSG_UITYPE, SyndicationFeed.UITYPE_JSPUI); for (String selector : SyndicationFeed.getDescriptionSelectors()) { labelMap.put("metadata." + selector, labels.getString(SyndicationFeed.MSG_METADATA + selector)); } return labelMap; } }