blob: 0c4018a22079d1c7257d120922cda9c836a6b7e6 [file] [log] [blame]
package net.sf.taverna.biocatalogue.model.search;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
//import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.biocatalogue.x2009.xml.rest.CollectionCoreStatistics;
import org.biocatalogue.x2009.xml.rest.ResourceLink;
import com.google.gson.Gson;
import net.sf.taverna.biocatalogue.model.Pair;
import net.sf.taverna.biocatalogue.model.Tag;
import net.sf.taverna.biocatalogue.model.Util;
import net.sf.taverna.biocatalogue.model.connectivity.BeanForPOSTToFilteredIndex;
import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueAPIRequest;
import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
import net.sf.taverna.biocatalogue.model.search.SearchInstance.TYPE;
import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsRenderer;
import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
import net.sf.taverna.biocatalogue.model.Resource;
/**
* @author Sergejs Aleksejevs
*/
public class SearchEngine
{
private static Logger logger = Logger.getLogger(SearchEngine.class);
protected SearchInstance searchInstance;
protected final BioCatalogueClient client;
protected final SearchInstanceTracker activeSearchInstanceTracker;
protected final CountDownLatch doneSignal;
protected final SearchResultsRenderer renderer;
public SearchEngine(SearchInstance searchInstance,
SearchInstanceTracker activeSearchInstanceTracker,
CountDownLatch doneSignal,
SearchResultsRenderer renderer)
{
this.searchInstance = searchInstance;
this.client = BioCatalogueClient.getInstance();
this.activeSearchInstanceTracker = activeSearchInstanceTracker;
this.doneSignal = doneSignal;
this.renderer = renderer;
}
/**
* @return <code>true</code> if the thread launched by this search engine is still
* the one treated as 'active' in the context of the user actions in the plugin;<br/>
* <code>false</code> - otherwise.
*/
protected boolean isCurrentSearch() {
return (activeSearchInstanceTracker.isCurrentSearchInstance(
this.searchInstance.getResourceTypeToSearchFor(), searchInstance));
}
/**
* Primary API request is the one that is *generated* when the search is first executed --
* for further requests (like fetching more data) it won't be fully generated, but rather
* will be derived from this primary one.
*/
protected BioCatalogueAPIRequest generateSearchRequest() {
return (generateSearchRequest(searchInstance.getSearchType()));
}
protected BioCatalogueAPIRequest generateSearchRequest(TYPE searchType)
{
// construct search request to execute on BioCatalogue server
BioCatalogueAPIRequest.TYPE requestType = BioCatalogueAPIRequest.TYPE.GET;
String requestURL = null;
String requestData = null;
switch (searchType) {
case QuerySearch:
requestURL = Util.appendURLParameter(searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndex(), "q", searchInstance.getSearchString());
break;
case TagSearch:
List<String> tags = new ArrayList<String>();
for (Tag t : searchInstance.getSearchTags()) {
tags.add(t.getFullTagName());
}
String tagParamValue = Util.join(tags, "[", "]", ",");
requestURL = Util.appendURLParameter(searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndex(), "tag", tagParamValue);
break;
case Filtering:
requestType = BioCatalogueAPIRequest.TYPE.POST;
// get search URL for the 'base' search upon which the filtering is based
requestURL = generateSearchRequest(searchInstance.getServiceFilteringBasedOn()).getURL();
requestURL = Util.appendStringBeforeParametersOfURL(requestURL, BioCatalogueClient.API_FILTERED_INDEX_SUFFIX, true);
// the base URL was prepared, now prepare filtering parameters as POST data
BeanForPOSTToFilteredIndex dataBean = new BeanForPOSTToFilteredIndex();
dataBean.filters = searchInstance.getFilteringSettings().getFilteringURLParameters();
Gson gson = new Gson();
requestData = gson.toJson(dataBean);
break;
}
// make sure that the URL was generated
if (requestURL == null) {
logger.error("Primary search URL couldn't be generated; Search engine must have encountered " +
"an unexpected search instance type: " + searchInstance.getSearchType());
return (null);
}
// Sort by name (for REST and SOAP only at the moment. Parent Web services do not have the sort by name facility yet.)
// if (!searchInstance.getResourceTypeToSearchFor().equals(net.sf.taverna.biocatalogue.model.Resource.TYPE.Service)){
requestURL = Util.appendURLParameter(requestURL, "sort_by", "name");
requestURL = Util.appendURLParameter(requestURL, "sort_order", "asc");
requestURL = Util.appendURLParameter(requestURL, "include", "ancestors");
// }
logger.info("Service Catalogue Plugin: Request URL for search: " + requestURL);
// append some search-type-independent parameters and return the URL
requestURL = Util.appendAllURLParameters(requestURL, searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndexAdditionalParameters());
return (new BioCatalogueAPIRequest(requestType, requestURL, requestData));
}
public void startNewSearch()
{
// construct API request for this search
BioCatalogueAPIRequest searchRequest = generateSearchRequest();
// perform the actual search operation
try
{
ResourceIndex resourceIndex = null;
if (searchRequest.getRequestType() == BioCatalogueAPIRequest.TYPE.GET) {
resourceIndex = client.getBioCatalogueResourceLiteIndex(searchInstance.getResourceTypeToSearchFor(), searchRequest.getURL());
}
else {
// can only be POST then!
resourceIndex = client.postBioCatalogueResourceLiteIndex(searchInstance.getResourceTypeToSearchFor(), searchRequest.getURL(), searchRequest.getData());
}
SearchResults searchResults = new SearchResults(searchInstance.getResourceTypeToSearchFor(), resourceIndex);
// only update search results of the associated search instance if the caller thread of
// this operation is still active - synchronisation helps to make sure that the results
// will definitely only be rendered if the current search instance is definitely active:
// this way searches finishing in quick succession will 'flash' the results for a short
// while before being updated, but that will happen in the correct order
synchronized (activeSearchInstanceTracker) {
if (isCurrentSearch()) {
searchInstance.setSearchResults(searchResults);
renderer.renderInitialResults(searchInstance);
}
}
}
catch (Exception e) {
logger.error("ERROR: Couldn't execute initial phase of a search by query, details below:", e);
}
// no matter if search was completed or interrupted by a new search, notify the caller // FIXME - is this needed?
searchCompleteNotifyCaller();
}
@SuppressWarnings("unchecked")
public void fetchMoreResults(int resultPageNumber)
{
if (resultPageNumber < 1 || resultPageNumber > searchInstance.getSearchResults().getTotalResultPageNumber()) {
logger.error("Prevented attempt to fetch an invalid result page: " + resultPageNumber + ". Returning...");
return;
}
// construct search URL to hit on BioCatalogue server --
// it is exactly as the one for the initial search, but with a page number
// parameter being added
BioCatalogueAPIRequest searchRequest = generateSearchRequest();
searchRequest.setURL(Util.appendURLParameter(searchRequest.getURL(), BioCatalogueClient.API_PAGE_PARAMETER, ""+resultPageNumber));
// fetch required result page
try
{
Pair<CollectionCoreStatistics,List<ResourceLink>> newResultBatch = client.getListOfItemsFromResourceCollectionIndex(
searchInstance.getResourceTypeToSearchFor().getXmlBeansGeneratedCollectionClass(), searchRequest);
int firstNewEntryIndex = searchInstance.getSearchResults().getFirstItemIndexOn(resultPageNumber);
searchInstance.getSearchResults().addSearchResults(newResultBatch.getSecondObject(), firstNewEntryIndex);
// only update search results of the associated search instance if the caller thread of
// this operation is still active
if (isCurrentSearch()) {
renderer.renderFurtherResults(searchInstance, firstNewEntryIndex, searchInstance.getResourceTypeToSearchFor().getApiResourceCountPerIndexPage());
}
}
catch (Exception e) {
// FIXME
}
// no matter if search was completed or interrupted by a new search, notify the caller // FIXME - is this needed?
searchCompleteNotifyCaller();
}
/**
* This method is used for notifying the object that has started the
* search of this particular search operation being complete.
*/
protected void searchCompleteNotifyCaller() {
this.doneSignal.countDown();
}
}