use of org.springframework.web.portlet.bind.annotation.ActionMapping in project uPortal by Jasig.
the class SearchPortletController method performSearch.
@SuppressWarnings("unchecked")
@ActionMapping
public void performSearch(@RequestParam(value = "query") String query, ActionRequest request, ActionResponse response, @RequestParam(value = "ajax", required = false) final boolean ajax) throws IOException {
final PortletSession session = request.getPortletSession();
final String queryId = RandomStringUtils.randomAlphanumeric(32);
Cache<String, Boolean> searchCounterCache;
synchronized (org.springframework.web.portlet.util.PortletUtils.getSessionMutex(session)) {
searchCounterCache = (Cache<String, Boolean>) session.getAttribute(SEARCH_COUNTER_NAME);
if (searchCounterCache == null) {
searchCounterCache = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.MINUTES).build();
session.setAttribute(SEARCH_COUNTER_NAME, searchCounterCache);
}
}
//Store the query id to track number of searches/minute
searchCounterCache.put(queryId, Boolean.TRUE);
if (searchCounterCache.size() > this.maximumSearchesPerMinute) {
//Make sure old data is expired
searchCounterCache.cleanUp();
//Too many searches in the last minute, fail the search
if (searchCounterCache.size() > this.maximumSearchesPerMinute) {
logger.debug("Rejecting search for '{}', exceeded max queries per minute for user", query);
if (!ajax) {
response.setRenderParameter("hitMaxQueries", Boolean.TRUE.toString());
response.setRenderParameter("query", query);
} else {
// For Ajax return to a nonexistent file to generate the 404 error since it was easier for the
// UI to have an error response.
final String contextPath = request.getContextPath();
response.sendRedirect(contextPath + AJAX_MAX_QUERIES_URL);
}
return;
}
}
// construct a new search query object from the string query
final SearchRequest queryObj = new SearchRequest();
queryObj.setQueryId(queryId);
queryObj.setSearchTerms(query);
// Create the session-shared results object
final PortalSearchResults results = new PortalSearchResults(defaultTabKey, resultTypeMappings);
// place the portal search results object in the session using the queryId to namespace it
Cache<String, PortalSearchResults> searchResultsCache;
synchronized (org.springframework.web.portlet.util.PortletUtils.getSessionMutex(session)) {
searchResultsCache = (Cache<String, PortalSearchResults>) session.getAttribute(SEARCH_RESULTS_CACHE_NAME);
if (searchResultsCache == null) {
searchResultsCache = CacheBuilder.newBuilder().maximumSize(20).expireAfterAccess(5, TimeUnit.MINUTES).build();
session.setAttribute(SEARCH_RESULTS_CACHE_NAME, searchResultsCache);
}
// Save the last queryId for an ajax autocomplete search response.
session.setAttribute(SEARCH_LAST_QUERY_ID, queryId);
}
searchResultsCache.put(queryId, results);
/*
* TODO: For autocomplete I wish we didn't have to go through a whole render phase just
* to trigger the events-based features of the portlet, but atm I don't
* see a way around it, since..
*
* - (1) You can only start an event chain in the Action phase; and
* - (2) You can only return JSON in a Resource phase; and
* - (3) An un-redirected Action phase leads to a Render phase, not a
* Resource phase :(
*
* It would be awesome either (first choice) to do Action > Event > Resource,
* or Action > sendRedirect() followed by a Resource request.
*
* As it stands, this implementation will trigger a complete render on
* the portal needlessly.
*/
// send a search query event
response.setEvent(SearchConstants.SEARCH_REQUEST_QNAME, queryObj);
logger.debug("Query initiated for queryId {}, query {}", queryId, query);
response.setRenderParameter("queryId", queryId);
response.setRenderParameter("query", query);
}
Aggregations