/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.app.webui.search; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; import java.sql.SQLException; import java.util.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.bulkedit.MetadataExport; import org.dspace.app.util.OpenSearch; import org.dspace.app.util.SyndicationFeed; import org.dspace.app.util.Util; import org.dspace.app.webui.servlet.SimpleSearchServlet; import org.dspace.app.webui.util.JSPManager; import org.dspace.app.webui.util.UIUtil; import org.dspace.authorize.AuthorizeManager; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.ItemIterator; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.I18nUtil; 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; import org.dspace.usage.UsageEvent; import org.dspace.usage.UsageSearchEvent; import org.dspace.utils.DSpace; import org.w3c.dom.Document; /** * @deprecated Since DSpace 4 the system use an abstraction layer named * Discovery to provide access to different search providers. The * legacy system built upon Apache Lucene is likely to be removed in * a future version. If you are interested in using Lucene as backend * for the DSpace search system, please consider to build a Lucene * implementation of the Discovery interfaces */ @Deprecated public class LuceneSearchRequestProcessor implements SearchRequestProcessor { private static final int ITEMMAP_RESULT_PAGE_SIZE = 50; /** log4j category */ private static Logger log = Logger.getLogger(SimpleSearchServlet.class); // locale-sensitive metadata labels private Map<String, Map<String, String>> localeLabels = null; private static String msgKey = "org.dspace.app.webui.servlet.FeedServlet"; private List<String> searchIndices = null; public synchronized void init() { if (localeLabels == null) { localeLabels = new HashMap<String, Map<String, String>>(); } if (searchIndices == null) { searchIndices = new ArrayList<String>(); String definition; int idx = 1; while ( ((definition = ConfigurationManager.getProperty("jspui.search.index.display." + idx))) != null){ String index = definition; searchIndices.add(index); idx++; } // backward compatibility if (searchIndices.size() == 0) { searchIndices.add("ANY"); searchIndices.add("author"); searchIndices.add("title"); searchIndices.add("keyword"); searchIndices.add("abstract"); searchIndices.add("series"); searchIndices.add("sponsor"); searchIndices.add("identifier"); searchIndices.add("language"); } } } /** * <p> * All metadata is search for the value contained in the "query" parameter. * If the "location" parameter is present, the user's location is switched * to that location using a redirect. Otherwise, the user's current location * is used to constrain the query; i.e., if the user is "in" a collection, * only results from the collection will be returned. * <p> * The value of the "location" parameter should be ALL (which means no * location), a the ID of a community (e.g. "123"), or a community ID, then * a slash, then a collection ID, e.g. "123/456". * * author: Robert Tansley */ @Override public void doSimpleSearch(Context context, HttpServletRequest request, HttpServletResponse response) throws SearchProcessorException, IOException, ServletException { try { // Get the query String query = request.getParameter("query"); int start = UIUtil.getIntParameter(request, "start"); String advanced = request.getParameter("advanced"); String fromAdvanced = request.getParameter("from_advanced"); int sortBy = UIUtil.getIntParameter(request, "sort_by"); String order = request.getParameter("order"); int rpp = UIUtil.getIntParameter(request, "rpp"); String advancedQuery = ""; // can't start earlier than 0 in the results! if (start < 0) { start = 0; } int collCount = 0; int commCount = 0; int itemCount = 0; Item[] resultsItems; Collection[] resultsCollections; Community[] resultsCommunities; QueryResults qResults = null; QueryArgs qArgs = new QueryArgs(); SortOption sortOption = null; if (request.getParameter("etal") != null) { qArgs.setEtAl(UIUtil.getIntParameter(request, "etal")); } try { if (sortBy > 0) { sortOption = SortOption.getSortOption(sortBy); qArgs.setSortOption(sortOption); } if (SortOption.ASCENDING.equalsIgnoreCase(order)) { qArgs.setSortOrder(SortOption.ASCENDING); } else { qArgs.setSortOrder(SortOption.DESCENDING); } } catch (Exception e) { } // Override the page setting if exporting metadata if ("submit_export_metadata".equals(UIUtil.getSubmitButton(request, "submit"))) { qArgs.setPageSize(Integer.MAX_VALUE); } else if (rpp > 0) { qArgs.setPageSize(rpp); } // if the "advanced" flag is set, build the query string from the // multiple query fields if (advanced != null) { query = qArgs.buildQuery(request); advancedQuery = qArgs.buildHTTPQuery(request); } // Ensure the query is non-null if (query == null) { query = ""; } // Get the location parameter, if any String location = request.getParameter("location"); // If there is a location parameter, we should redirect to // do the search with the correct location. if ((location != null) && !location.equals("")) { String url = ""; if (!location.equals("/")) { // Location is a Handle url = "/handle/" + location; } // Encode the query query = URLEncoder.encode(query, Constants.DEFAULT_ENCODING); if (advancedQuery.length() > 0) { query = query + "&from_advanced=true&" + advancedQuery; } // Do the redirect response.sendRedirect(response.encodeRedirectURL(request .getContextPath() + url + "/simple-search?query=" + query)); return; } // Build log information String logInfo = ""; // Get our location Community community = UIUtil.getCommunityLocation(request); Collection collection = UIUtil.getCollectionLocation(request); // get the start of the query results page // List resultObjects = null; qArgs.setQuery(query); qArgs.setStart(start); // Perform the search if (collection != null) { logInfo = "collection_id=" + collection.getID() + ","; // Values for drop-down box request.setAttribute("community", community); request.setAttribute("collection", collection); qResults = DSQuery.doQuery(context, qArgs, collection); } else if (community != null) { logInfo = "community_id=" + community.getID() + ","; request.setAttribute("community", community); // Get the collections within the community for the dropdown box request .setAttribute("collection.array", community .getCollections()); qResults = DSQuery.doQuery(context, qArgs, community); } else { // Get all communities for dropdown box Community[] communities = Community.findAll(context); request.setAttribute("community.array", communities); qResults = DSQuery.doQuery(context, qArgs); } // now instantiate the results and put them in their buckets for (int i = 0; i < qResults.getHitTypes().size(); i++) { Integer myType = qResults.getHitTypes().get(i); // add the handle to the appropriate lists switch (myType.intValue()) { case Constants.ITEM: itemCount++; break; case Constants.COLLECTION: collCount++; break; case Constants.COMMUNITY: commCount++; break; } } // Make objects from the handles - make arrays, fill them out resultsCommunities = new Community[commCount]; resultsCollections = new Collection[collCount]; resultsItems = new Item[itemCount]; collCount = 0; commCount = 0; itemCount = 0; for (int i = 0; i < qResults.getHitTypes().size(); i++) { Integer myId = qResults.getHitIds().get(i); String myHandle = qResults.getHitHandles().get(i); Integer myType = qResults.getHitTypes().get(i); // add the handle to the appropriate lists switch (myType.intValue()) { case Constants.ITEM: if (myId != null) { resultsItems[itemCount] = Item.find(context, myId); } else { resultsItems[itemCount] = (Item)HandleManager.resolveToObject(context, myHandle); } if (resultsItems[itemCount] == null) { throw new SQLException("Query \"" + query + "\" returned unresolvable item"); } itemCount++; break; case Constants.COLLECTION: if (myId != null) { resultsCollections[collCount] = Collection.find(context, myId); } else { resultsCollections[collCount] = (Collection)HandleManager.resolveToObject(context, myHandle); } if (resultsCollections[collCount] == null) { throw new SQLException("Query \"" + query + "\" returned unresolvable collection"); } collCount++; break; case Constants.COMMUNITY: if (myId != null) { resultsCommunities[commCount] = Community.find(context, myId); } else { resultsCommunities[commCount] = (Community)HandleManager.resolveToObject(context, myHandle); } if (resultsCommunities[commCount] == null) { throw new SQLException("Query \"" + query + "\" returned unresolvable community"); } commCount++; break; } } // Log log.info(LogManager.getHeader(context, "search", logInfo + "query=\"" + query + "\",results=(" + resultsCommunities.length + "," + resultsCollections.length + "," + resultsItems.length + ")")); // Pass in some page qualities // total number of pages int pageTotal = 1 + ((qResults.getHitCount() - 1) / qResults .getPageSize()); // current page being displayed int pageCurrent = 1 + (qResults.getStart() / qResults.getPageSize()); // pageLast = min(pageCurrent+9,pageTotal) int pageLast = ((pageCurrent + 9) > pageTotal) ? pageTotal : (pageCurrent + 9); // pageFirst = max(1,pageCurrent-9) int pageFirst = ((pageCurrent - 9) > 1) ? (pageCurrent - 9) : 1; //Fire an event to log our search DSpaceObject scope = null; if(collection != null){ scope = collection; }else if(community != null){ scope = community; } logSearch(context, request, query, pageCurrent, scope); // Pass the results to the display JSP request.setAttribute("items", resultsItems); request.setAttribute("communities", resultsCommunities); request.setAttribute("collections", resultsCollections); request.setAttribute("pagetotal", Integer.valueOf(pageTotal)); request.setAttribute("pagecurrent", Integer.valueOf(pageCurrent)); request.setAttribute("pagelast", Integer.valueOf(pageLast)); request.setAttribute("pagefirst", Integer.valueOf(pageFirst)); request.setAttribute("queryresults", qResults); // And the original query string request.setAttribute("query", query); request.setAttribute("order", qArgs.getSortOrder()); request.setAttribute("sortedBy", sortOption); if (AuthorizeManager.isAdmin(context)) { // Set a variable to create admin buttons request.setAttribute("admin_button", Boolean.TRUE); } if ((fromAdvanced != null) && (qResults.getHitCount() == 0)) { // send back to advanced form if no results Community[] communities = Community.findAll(context); request.setAttribute("communities", communities); request.setAttribute("no_results", "yes"); Map<String, String> queryHash = qArgs.buildQueryMap(request); if (queryHash != null) { for (Map.Entry<String, String> entry : queryHash.entrySet()) { request.setAttribute(entry.getKey(), entry.getValue()); } } JSPManager.showJSP(request, response, "/search/advanced.jsp"); } else if ("submit_export_metadata".equals(UIUtil.getSubmitButton(request, "submit"))) { exportMetadata(context, response, resultsItems); } else { JSPManager.showJSP(request, response, "/search/results.jsp"); } } catch (IllegalStateException e) { throw new SearchProcessorException(e.getMessage(), e); } catch (SQLException e) { throw new SearchProcessorException(e.getMessage(), e); } } protected void logSearch(Context context, HttpServletRequest request, String query, int start, DSpaceObject scope) { UsageSearchEvent searchEvent = new UsageSearchEvent( UsageEvent.Action.SEARCH, request, context, null, Arrays.asList(query), scope); if(!StringUtils.isBlank(request.getParameter("rpp"))){ searchEvent.setRpp(Integer.parseInt(request.getParameter("rpp"))); } if(!StringUtils.isBlank(request.getParameter("sort_by"))){ searchEvent.setSortBy(request.getParameter("sort_by")); } if(!StringUtils.isBlank(request.getParameter("order"))){ searchEvent.setSortOrder(request.getParameter("order")); } if(!StringUtils.isBlank(request.getParameter("start"))){ searchEvent.setPage(start); } //Fire our event new DSpace().getEventService().fireEvent(searchEvent); } /** * Method for constructing the advanced search form * * author: gam */ @Override public void doAdvancedSearch(Context context, HttpServletRequest request, HttpServletResponse response) throws SearchProcessorException, ServletException, IOException { // just build a list of top-level communities and pass along to the jsp Community[] communities; try { communities = Community.findAllTop(context); } catch (SQLException e) { throw new SearchProcessorException(e.getMessage(), e); } request.setAttribute("communities", communities); JSPManager.showJSP(request, response, "/search/advanced.jsp"); } /** * Method for producing OpenSearch-compliant search results, and the * OpenSearch description document. * <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 */ @Override public void doOpenSearch(Context context, HttpServletRequest request, HttpServletResponse response) throws SearchProcessorException, IOException, ServletException { init(); // 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; try { container = (scope != null) ? HandleManager.resolveToObject(context, scope) : null; } catch (IllegalStateException e) { throw new SearchProcessorException(e.getMessage(), e); } catch (SQLException e) { throw new SearchProcessorException(e.getMessage(), e); } // 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); } else { throw new IllegalStateException("Invalid container for search context"); } // now instantiate the results DSpaceObject[] results = new DSpaceObject[qResults.getHitHandles().size()]; for (int i = 0; i < qResults.getHitHandles().size(); i++) { String myHandle = qResults.getHitHandles().get(i); DSpaceObject dso; try { dso = HandleManager.resolveToObject(context, myHandle); } catch (IllegalStateException e) { throw new SearchProcessorException(e.getMessage(), e); } catch (SQLException e) { throw new SearchProcessorException(e.getMessage(), e); } if (dso == null) { throw new SearchProcessorException("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.getHitCount(), qResults.getStart(), qResults.getPageSize(), 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(), e); } } /** * Method for searching authors in item map * * author: gam */ @Override public void doItemMapSearch(Context context, HttpServletRequest request, HttpServletResponse response) throws SearchProcessorException, ServletException, IOException { String query = (String) request.getParameter("query"); int page = UIUtil.getIntParameter(request, "page")-1; int offset = page > 0? page * ITEMMAP_RESULT_PAGE_SIZE:0; Collection collection = (Collection) request.getAttribute("collection"); String idx = (String) request.getParameter("index"); if (StringUtils.isNotBlank(idx) && !idx.equalsIgnoreCase("any")) { query = idx + ":(" + query + ")"; } QueryArgs queryArgs = new QueryArgs(); queryArgs.setQuery(query + " -location:l" + collection.getID()); queryArgs.setPageSize(ITEMMAP_RESULT_PAGE_SIZE); queryArgs.setStart(offset); QueryResults results = DSQuery.doQuery(context, queryArgs); Map<Integer, Item> items = new HashMap<Integer, Item>(); List<String> handles = results.getHitHandles(); try { for (String handle : handles) { DSpaceObject resultDSO = HandleManager.resolveToObject(context, handle); if (resultDSO.getType() == Constants.ITEM) { Item item = (Item) resultDSO; if (AuthorizeManager.authorizeActionBoolean(context, item, Constants.READ)) { items.put(Integer.valueOf(item.getID()), item); } } } } catch (SQLException e) { throw new SearchProcessorException(e.getMessage(), e); } request.setAttribute("browsetext", query); request.setAttribute("items", items); request.setAttribute("more", results.getHitCount() > offset + ITEMMAP_RESULT_PAGE_SIZE); request.setAttribute("browsetype", "Add"); request.setAttribute("page", page > 0 ? page + 1 : 1); JSPManager.showJSP(request, response, "itemmap-browse.jsp"); } /** * Export the search results as a csv file * * @param context The DSpace context * @param response The request object * @param items The result items * @throws IOException * @throws ServletException */ protected void exportMetadata(Context context, HttpServletResponse response, Item[] items) throws IOException, ServletException { // Log the attempt log.info(LogManager.getHeader(context, "metadataexport", "exporting_search")); // Export a search view List<Integer> iids = new ArrayList<Integer>(); for (Item item : items) { iids.add(item.getID()); } ItemIterator ii = new ItemIterator(context, iids); MetadataExport exporter = new MetadataExport(context, ii, false); // Perform the export DSpaceCSV csv = exporter.export(); // Return the csv file response.setContentType("text/csv; charset=UTF-8"); response.setHeader("Content-Disposition", "attachment; filename=search-results.csv"); PrintWriter out = response.getWriter(); out.write(csv.toString()); out.flush(); out.close(); log.info(LogManager.getHeader(context, "metadataexport", "exported_file:search-results.csv")); return; } private Map<String, String> getLabels(HttpServletRequest request) { // Get access to the localized resource bundle Locale locale = UIUtil.getSessionLocale(request); 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>(); labelMap.put(SyndicationFeed.MSG_UNTITLED, I18nUtil.getMessage(msgKey + ".notitle", locale)); labelMap.put(SyndicationFeed.MSG_LOGO_TITLE, I18nUtil.getMessage(msgKey + ".logo.title", locale)); labelMap.put(SyndicationFeed.MSG_FEED_DESCRIPTION, I18nUtil.getMessage(msgKey + ".general-feed.description", locale)); labelMap.put(SyndicationFeed.MSG_UITYPE, SyndicationFeed.UITYPE_JSPUI); for (String selector : SyndicationFeed.getDescriptionSelectors()) { labelMap.put("metadata." + selector, I18nUtil.getMessage(SyndicationFeed.MSG_METADATA + selector, locale)); } return labelMap; } @Override public String getI18NKeyPrefix() { return "jsp.search.advanced.type."; } @Override public List<String> getSearchIndices() { init(); return searchIndices; } }