/**
* 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.discovery;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
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.webui.search.SearchProcessorException;
import org.dspace.app.webui.search.SearchRequestProcessor;
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.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.SearchUtils;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoverySearchFilter;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
import org.w3c.dom.Document;
public class DiscoverySearchRequestProcessor implements SearchRequestProcessor
{
private static final int ITEMMAP_RESULT_PAGE_SIZE = 50;
private static String msgKey = "org.dspace.app.webui.servlet.FeedServlet";
/** log4j category */
private static Logger log = Logger.getLogger(DiscoverySearchRequestProcessor.class);
// locale-sensitive metadata labels
private Map<String, Map<String, String>> localeLabels = null;
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>();
DiscoveryConfiguration discoveryConfiguration = SearchUtils
.getDiscoveryConfiguration();
searchIndices.add("any");
for (DiscoverySearchFilter sFilter : discoveryConfiguration.getSearchFilters())
{
searchIndices.add(sFilter.getIndexFieldName());
}
}
}
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
DSpaceObject container;
try
{
container = DiscoverUtility.getSearchScope(context,
request);
}
catch (Exception e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
DiscoverQuery queryArgs = DiscoverUtility.getDiscoverQuery(context,
request, container, false);
String query = queryArgs.getQuery();
// Perform the search
DiscoverResult qResults = null;
try
{
qResults = SearchUtils.getSearchService().search(context,
container, queryArgs);
}
catch (SearchServiceException e)
{
log.error(
LogManager.getHeader(context, "opensearch", "query="
+ queryArgs.getQuery() + ",scope=" + scope
+ ",error=" + e.getMessage()), e);
throw new RuntimeException(e.getMessage(), e);
}
// Log
log.info(LogManager.getHeader(context, "opensearch",
"scope=" + scope + ",query=\"" + query + "\",results=("
+ qResults.getTotalSearchResults() + ")"));
// format and return results
Map<String, String> labelMap = getLabels(request);
DSpaceObject[] dsoResults = new DSpaceObject[qResults
.getDspaceObjects().size()];
qResults.getDspaceObjects().toArray(dsoResults);
Document resultsDoc = OpenSearch.getResultsDoc(format, query,
(int)qResults.getTotalSearchResults(), qResults.getStart(),
qResults.getMaxResults(), container, dsoResults, 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 = 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;
}
public void doSimpleSearch(Context context, HttpServletRequest request,
HttpServletResponse response) throws SearchProcessorException,
IOException, ServletException
{
Item[] resultsItems;
Collection[] resultsCollections;
Community[] resultsCommunities;
DSpaceObject scope;
try
{
scope = DiscoverUtility.getSearchScope(context, request);
}
catch (IllegalStateException e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
catch (SQLException e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
DiscoveryConfiguration discoveryConfiguration = SearchUtils
.getDiscoveryConfiguration(scope);
List<DiscoverySortFieldConfiguration> sortFields = discoveryConfiguration
.getSearchSortConfiguration().getSortFields();
List<String> sortOptions = new ArrayList<String>();
for (DiscoverySortFieldConfiguration sortFieldConfiguration : sortFields)
{
String sortField = SearchUtils.getSearchService().toSortFieldIndex(
sortFieldConfiguration.getMetadataField(),
sortFieldConfiguration.getType());
sortOptions.add(sortField);
}
request.setAttribute("sortOptions", sortOptions);
DiscoverQuery queryArgs = DiscoverUtility.getDiscoverQuery(context,
request, scope, true);
queryArgs.setSpellCheck(discoveryConfiguration.isSpellCheckEnabled());
List<DiscoverySearchFilterFacet> availableFacet = discoveryConfiguration
.getSidebarFacets();
request.setAttribute("facetsConfig",
availableFacet != null ? availableFacet
: new ArrayList<DiscoverySearchFilterFacet>());
int etal = UIUtil.getIntParameter(request, "etal");
if (etal == -1)
{
etal = ConfigurationManager
.getIntProperty("webui.itemlist.author-limit");
}
request.setAttribute("etal", etal);
String query = queryArgs.getQuery();
request.setAttribute("query", query);
request.setAttribute("queryArgs", queryArgs);
List<DiscoverySearchFilter> availableFilters = discoveryConfiguration
.getSearchFilters();
request.setAttribute("availableFilters", availableFilters);
List<String[]> appliedFilters = DiscoverUtility.getFilters(request);
request.setAttribute("appliedFilters", appliedFilters);
List<String> appliedFilterQueries = new ArrayList<String>();
for (String[] filter : appliedFilters)
{
appliedFilterQueries.add(filter[0] + "::" + filter[1] + "::"
+ filter[2]);
}
request.setAttribute("appliedFilterQueries", appliedFilterQueries);
List<DSpaceObject> scopes = new ArrayList<DSpaceObject>();
if (scope == null)
{
Community[] topCommunities;
try
{
topCommunities = Community.findAllTop(context);
}
catch (SQLException e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
for (Community com : topCommunities)
{
scopes.add(com);
}
}
else
{
try
{
DSpaceObject pDso = scope.getParentObject();
while (pDso != null)
{
// add to the available scopes in reverse order
scopes.add(0, pDso);
pDso = pDso.getParentObject();
}
scopes.add(scope);
if (scope instanceof Community)
{
Community[] comms = ((Community) scope).getSubcommunities();
for (Community com : comms)
{
scopes.add(com);
}
Collection[] colls = ((Community) scope).getCollections();
for (Collection col : colls)
{
scopes.add(col);
}
}
}
catch (SQLException e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
}
request.setAttribute("scope", scope);
request.setAttribute("scopes", scopes);
// Perform the search
DiscoverResult qResults = null;
try
{
qResults = SearchUtils.getSearchService().search(context, scope,
queryArgs);
List<Community> resultsListComm = new ArrayList<Community>();
List<Collection> resultsListColl = new ArrayList<Collection>();
List<Item> resultsListItem = new ArrayList<Item>();
for (DSpaceObject dso : qResults.getDspaceObjects())
{
if (dso instanceof Item)
{
resultsListItem.add((Item) dso);
}
else if (dso instanceof Collection)
{
resultsListColl.add((Collection) dso);
}
else if (dso instanceof Community)
{
resultsListComm.add((Community) dso);
}
}
// Make objects from the handles - make arrays, fill them out
resultsCommunities = new Community[resultsListComm.size()];
resultsCollections = new Collection[resultsListColl.size()];
resultsItems = new Item[resultsListItem.size()];
resultsCommunities = resultsListComm.toArray(resultsCommunities);
resultsCollections = resultsListColl.toArray(resultsCollections);
resultsItems = resultsListItem.toArray(resultsItems);
// Log
log.info(LogManager.getHeader(context, "search", "scope=" + scope
+ ",query=\"" + query + "\",results=("
+ resultsCommunities.length + ","
+ resultsCollections.length + "," + resultsItems.length
+ ")"));
// Pass in some page qualities
// total number of pages
long pageTotal = 1 + ((qResults.getTotalSearchResults() - 1) / qResults
.getMaxResults());
// current page being displayed
long pageCurrent = 1 + (qResults.getStart() / qResults
.getMaxResults());
// pageLast = min(pageCurrent+3,pageTotal)
long pageLast = ((pageCurrent + 3) > pageTotal) ? pageTotal
: (pageCurrent + 3);
// pageFirst = max(1,pageCurrent-3)
long pageFirst = ((pageCurrent - 3) > 1) ? (pageCurrent - 3) : 1;
// Pass the results to the display JSP
request.setAttribute("items", resultsItems);
request.setAttribute("communities", resultsCommunities);
request.setAttribute("collections", resultsCollections);
request.setAttribute("pagetotal", new Long(pageTotal));
request.setAttribute("pagecurrent", new Long(pageCurrent));
request.setAttribute("pagelast", new Long(pageLast));
request.setAttribute("pagefirst", new Long(pageFirst));
request.setAttribute("spellcheck", qResults.getSpellCheckQuery());
request.setAttribute("queryresults", qResults);
try
{
if (AuthorizeManager.isAdmin(context))
{
// Set a variable to create admin buttons
request.setAttribute("admin_button", new Boolean(true));
}
}
catch (SQLException e)
{
throw new SearchProcessorException(e.getMessage(), e); }
if ("submit_export_metadata".equals(UIUtil.getSubmitButton(request,
"submit")))
{
exportMetadata(context, response, resultsItems);
}
}
catch (SearchServiceException e)
{
log.error(
LogManager.getHeader(context, "search", "query="
+ queryArgs.getQuery() + ",scope=" + scope
+ ",error=" + e.getMessage()), e);
request.setAttribute("search.error", true);
request.setAttribute("search.error.message", e.getMessage());
}
JSPManager.showJSP(request, response, "/search/discovery.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
ArrayList iids = new ArrayList();
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;
}
/**
* Method for constructing the discovery advanced search form
*
* author: Andrea Bollini
*/
@Override
public void doAdvancedSearch(Context context, HttpServletRequest request,
HttpServletResponse response) throws SearchProcessorException,
IOException, ServletException
{
// just redirect to the simple search servlet.
// The advanced form is always displayed with Discovery togheter with
// the search result
// the first access to the advanced form performs a search for
// "anythings" (SOLR *:*)
response.sendRedirect(request.getContextPath() + "/simple-search");
}
/**
* Method for searching authors in item map
*
* author: gam
*/
@Override
public void doItemMapSearch(Context context, HttpServletRequest request,
HttpServletResponse response) throws SearchProcessorException, ServletException, IOException
{
String queryString = (String) request.getParameter("query");
Collection collection = (Collection) request.getAttribute("collection");
int page = UIUtil.getIntParameter(request, "page")-1;
int offset = page > 0? page * ITEMMAP_RESULT_PAGE_SIZE:0;
String idx = (String) request.getParameter("index");
if (StringUtils.isNotBlank(idx) && !idx.equalsIgnoreCase("any"))
{
queryString = idx + ":(" + queryString + ")";
}
DiscoverQuery query = new DiscoverQuery();
query.setQuery(queryString);
query.addFilterQueries("-location:l"+collection.getID());
query.setMaxResults(ITEMMAP_RESULT_PAGE_SIZE);
query.setStart(offset);
DiscoverResult results = null;
try
{
results = SearchUtils.getSearchService().search(context, query);
}
catch (SearchServiceException e)
{
throw new SearchProcessorException(e.getMessage(), e);
}
Map<Integer, Item> items = new HashMap<Integer, Item>();
List<DSpaceObject> resultDSOs = results.getDspaceObjects();
for (DSpaceObject dso : resultDSOs)
{
if (dso != null && dso.getType() == Constants.ITEM)
{
// no authorization check is required as discovery is right aware
Item item = (Item) dso;
items.put(Integer.valueOf(item.getID()), item);
}
}
request.setAttribute("browsetext", queryString);
request.setAttribute("items", items);
request.setAttribute("more", results.getTotalSearchResults() > offset + ITEMMAP_RESULT_PAGE_SIZE);
request.setAttribute("browsetype", "Add");
request.setAttribute("page", page > 0 ? page + 1 : 1);
JSPManager.showJSP(request, response, "itemmap-browse.jsp");
}
@Override
public String getI18NKeyPrefix()
{
return "jsp.search.filter.";
}
@Override
public List<String> getSearchIndices()
{
init();
return searchIndices;
}
}