You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/03/20 15:22:31 UTC
[17/51] [abbrv] [partial] incubator-taverna-workbench git commit:
taverna-workbench-* -> taverna-*
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
new file mode 100644
index 0000000..0e033cc
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
@@ -0,0 +1,785 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import java.io.*;
+import java.net.*;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.Pair;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.SoapOperationPortIdentity;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config.BioCataloguePluginConfiguration;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.Annotations;
+import org.biocatalogue.x2009.xml.rest.AnnotationsDocument;
+import org.biocatalogue.x2009.xml.rest.CollectionCoreStatistics;
+import org.biocatalogue.x2009.xml.rest.Filters;
+import org.biocatalogue.x2009.xml.rest.FiltersDocument;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.RestMethodDocument;
+import org.biocatalogue.x2009.xml.rest.RestMethods;
+import org.biocatalogue.x2009.xml.rest.RestMethodsDocument;
+import org.biocatalogue.x2009.xml.rest.Search;
+import org.biocatalogue.x2009.xml.rest.SearchDocument;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceDocument;
+import org.biocatalogue.x2009.xml.rest.ServiceProvider;
+import org.biocatalogue.x2009.xml.rest.ServiceProviderDocument;
+import org.biocatalogue.x2009.xml.rest.ServiceProviders;
+import org.biocatalogue.x2009.xml.rest.ServiceProvidersDocument;
+import org.biocatalogue.x2009.xml.rest.Services;
+import org.biocatalogue.x2009.xml.rest.ServicesDocument;
+import org.biocatalogue.x2009.xml.rest.SoapInput;
+import org.biocatalogue.x2009.xml.rest.SoapInputDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+import org.biocatalogue.x2009.xml.rest.SoapOperationDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOperations;
+import org.biocatalogue.x2009.xml.rest.SoapOperationsDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOutput;
+import org.biocatalogue.x2009.xml.rest.SoapOutputDocument;
+import org.biocatalogue.x2009.xml.rest.SoapService;
+import org.biocatalogue.x2009.xml.rest.SoapServiceDocument;
+import org.biocatalogue.x2009.xml.rest.Tag;
+import org.biocatalogue.x2009.xml.rest.TagDocument;
+import org.biocatalogue.x2009.xml.rest.Tags;
+import org.biocatalogue.x2009.xml.rest.TagsDocument;
+import org.biocatalogue.x2009.xml.rest.User;
+import org.biocatalogue.x2009.xml.rest.UserDocument;
+import org.biocatalogue.x2009.xml.rest.Users;
+import org.biocatalogue.x2009.xml.rest.UsersDocument;
+
+import com.google.gson.Gson;
+
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueClient
+{
+ // ******* CONSTANTS *******
+ // plugin details
+ public static final String PLUGIN_VERSION = "0.1.1";
+ public static final String PLUGIN_USER_AGENT = "Taverna2-ServiceCatalogue-plugin/" +
+ PLUGIN_VERSION +
+ " Java/" + System.getProperty("java.version");
+
+ public static final String XML_MIME_TYPE = "application/xml";
+ public static final String JSON_MIME_TYPE = "application/json";
+ public static final String LITE_JSON_MIME_TYPE = "application/biocat-lite+json";
+
+ public static final String XML_DATA_FORMAT = ".xml";
+ public static final String JSON_DATA_FORMAT = ".json";
+ public static final String LITE_JSON_DATA_FORMAT = ".bljson";
+
+
+
+ // API URLs
+ public static final String DEFAULT_API_SANDBOX_BASE_URL = "http://sandbox.biocatalogue.org";
+ public static final String DEFAULT_API_TEST_SERVER_BASE_URL = "http://test.biocatalogue.org";
+ public static final String DEFAULT_API_LIVE_SERVER_BASE_URL = "http://www.biocatalogue.org";
+
+ private static String BASE_URL; // BioCatalogue base URL to use (can be updated at runtime)
+
+ public static String API_REGISTRIES_URL;
+ public static String API_SERVICE_PROVIDERS_URL;
+ public static String API_USERS_URL;
+ public static String API_USER_FILTERS_URL;
+ public static String API_SERVICES_URL;
+ public static String API_SERVICE_FILTERS_URL;
+ public static String API_SOAP_OPERATIONS_URL;
+ public static String API_SOAP_OPERATION_FILTERS_URL;
+ public static String API_REST_METHODS_URL;
+ public static String API_REST_METHOD_FILTERS_URL;
+ public static String API_TAG_CLOUD_URL;
+ public static String API_SEARCH_URL;
+ public static String API_LOOKUP_URL;
+
+ // URL modifiers
+ public static final Map<String,String> API_INCLUDE_SUMMARY = Collections.singletonMap("include","summary"); // for fetching Service
+ public static final Map<String,String> API_INCLUDE_ANCESTORS = Collections.singletonMap("include", "ancestors,inputs,outputs"); // for fetching SOAP Operations and REST Methods
+ public static final String[] API_SORT_BY_NAME = {"sort","name"}; // for tag cloud
+ public static final String[] API_SORT_BY_COUNTS = {"sort","counts"}; // for tag cloud
+ public static final String[] API_ALSO_INPUTS_OUTPUTS = {"also","inputs,outputs"}; // for annotations on SOAP operation
+
+ public static final String API_PER_PAGE_PARAMETER = "per_page";
+ public static final String API_PAGE_PARAMETER = "page";
+ public static final String API_LIMIT_PARAMETER = "limit";
+ public static final String API_SERVICE_MONITORING_URL_SUFFIX = "/monitoring";
+ public static final String API_FILTERED_INDEX_SUFFIX = "/filtered_index";
+
+ // API Request scope
+ public static final String API_SCOPE_PARAMETER = "scope";
+ public static final String API_SCOPE_SOAP_OPERATIONS = "soap_operations";
+ public static final String API_SCOPE_REST_METHODS = "rest_methods";
+ public static final String API_SCOPE_SERVICES = "services";
+ public static final String API_SCOPE_SERVICE_PROVIDERS = "service_providers";
+ public static final String API_SCOPE_REGISTRIES = "registries";
+ public static final String API_SCOPE_USERS = "users";
+
+ public static final String API_TAG_PARAMETER = "tag";
+
+ public static final String API_LOOKUP_WSDL_LOCATION_PARAMETER = "wsdl_location";
+ public static final String API_LOOKUP_OPERATION_NAME_PARAMETER = "operation_name";
+ public static final String API_LOOKUP_SOAP_INPUT_NAME_PARAMETER = "input_name";
+ public static final String API_LOOKUP_SOAP_OUTPUT_NAME_PARAMETER = "output_name";
+
+
+ // *************************
+
+ // universal date formatters
+ private static final DateFormat DATE_FORMATTER = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
+ private static final DateFormat SHORT_DATE_FORMATTER = new SimpleDateFormat("HH:mm 'on' dd/MM/yyyy");
+ private static final DateFormat API_LOGGING_TIMESTAMP_FORMATTER = DateFormat.getDateTimeInstance();
+
+
+ // SETTINGS
+ private Properties iniSettings; // settings that are read/stored from/to INI file
+
+ private File fAPIOperationLog;
+ private PrintWriter pwAPILogWriter;
+
+ // the logger
+ private Logger logger = Logger.getLogger(BioCatalogueClient.class);
+
+ private static BioCatalogueClient INSTANCE;
+
+ // default constructor
+ private BioCatalogueClient()
+ {
+ // TODO: load any config settings (if necessary)
+
+ // load the BioCatalogue API base URL from the plugin's configuration settings
+ this.setBaseURL(BioCataloguePluginConfiguration.getInstance().
+ getProperty(BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL));
+
+ // open API operation log file, if necessary
+ if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING ||
+ BioCataloguePluginConstants.PERFORM_API_XML_DATA_BINDING_TIME_LOGGING )
+ {
+ try {
+ BioCataloguePluginConstants.LOG_FILE_FOLDER.mkdirs(); // just in case this log file was never written - create the folder as well
+ fAPIOperationLog = new File(BioCataloguePluginConstants.LOG_FILE_FOLDER,
+ BioCataloguePluginConstants.API_OPERATION_LOG_FILENAME);
+ pwAPILogWriter = new PrintWriter(new FileOutputStream(fAPIOperationLog, true), true); // auto-flush makes sure that even if app crashes, log will not be lost
+ }
+ catch (NullPointerException e) {
+ pwAPILogWriter = new PrintWriter(System.out, true);
+ logger.error("ERROR: Folder to log API operation details is unknown (using System.out instead)... Details:", e);
+ }
+ catch (FileNotFoundException e) {
+ logger.error("ERROR: Couldn't open API operation log file... Details:", e);
+ }
+ }
+ }
+
+ public static synchronized BioCatalogueClient getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new BioCatalogueClient();
+ }
+ return INSTANCE;
+ }
+
+
+ public String getBaseURL() {
+ return this.BASE_URL;
+ }
+
+ /**
+ * Updates the base API URL and also
+ * updates derived URLs of sub-URLs
+ * (e.g. BASE_URL + /services, etc)
+ *
+ * @param baseURL The new value for the BioCatalogue API base URL.
+ */
+ public void setBaseURL(String baseURL)
+ {
+ // make sure the base URL doesn't have a slash at the end
+ // (otherwise double slashes may occur during URL manipulation)
+ while (baseURL.endsWith("/")) { baseURL = baseURL.substring(0, baseURL.length() - 1); }
+
+ this.BASE_URL = baseURL;
+
+ API_REGISTRIES_URL = BASE_URL + "/registries";
+ API_SERVICE_PROVIDERS_URL = BASE_URL + "/service_providers";
+ API_USERS_URL = BASE_URL + "/users";
+ API_USER_FILTERS_URL = API_USERS_URL + "/filters";
+ API_SERVICES_URL = BASE_URL + "/services";
+ API_SERVICE_FILTERS_URL = API_SERVICES_URL + "/filters";
+ API_SOAP_OPERATIONS_URL = BASE_URL + "/soap_operations";
+ API_SOAP_OPERATION_FILTERS_URL = API_SOAP_OPERATIONS_URL + "/filters";
+ API_REST_METHODS_URL = BASE_URL + "/rest_methods";
+ API_REST_METHOD_FILTERS_URL = API_REST_METHODS_URL + "/filters";
+ API_TAG_CLOUD_URL = BASE_URL + "/tags";
+ API_SEARCH_URL = BASE_URL + "/search";
+ API_LOOKUP_URL = BASE_URL + "/lookup";
+ }
+
+ public File getAPIOperationLog() {
+ return fAPIOperationLog;
+ }
+
+ public PrintWriter getAPILogWriter() {
+ return pwAPILogWriter;
+ }
+
+
+ // ************ METHODS FOR RETRIEVAL OF SPECIALISED OBJECT FROM THE API VIA XML ************
+
+ public Annotations getBioCatalogueAnnotations(String strAnnotationsURL) throws Exception {
+ return (parseAPIResponseStream(Annotations.class, doBioCatalogueGET(strAnnotationsURL)));
+ }
+
+ public Filters getBioCatalogueFilters(String strURL) throws Exception {
+ return (parseAPIResponseStream(Filters.class, doBioCatalogueGET(strURL)));
+ }
+
+ public Services getBioCatalogueServices(String strURL) throws Exception {
+ return (parseAPIResponseStream(Services.class, doBioCatalogueGET(strURL)));
+ }
+
+ public Service getBioCatalogueService(String serviceURL) throws Exception {
+ return (parseAPIResponseStream(Service.class, doBioCatalogueGET(serviceURL)));
+ }
+
+ public Service getBioCatalogueServiceSummary(String serviceURL) throws Exception {
+ return (parseAPIResponseStream(Service.class, doBioCatalogueGET(Util.appendAllURLParameters(serviceURL, API_INCLUDE_SUMMARY))));
+ }
+
+ public Service getBioCatalogueServiceMonitoringData(String serviceURL) throws Exception
+ {
+ return (parseAPIResponseStream(Service.class,
+ doBioCatalogueGET(serviceURL + API_SERVICE_MONITORING_URL_SUFFIX))
+ );
+ }
+
+ public SoapService getBioCatalogueSoapService(String soapServiceURL) throws Exception {
+ return (parseAPIResponseStream(SoapService.class, doBioCatalogueGET(soapServiceURL)));
+ }
+
+ public SoapOperation getBioCatalogueSoapOperation(String soapOperationURL) throws Exception {
+ return (parseAPIResponseStream(SoapOperation.class, doBioCatalogueGET(soapOperationURL)));
+ }
+
+ public RestMethod getBioCatalogueRestMethod(String restMethodURL) throws Exception {
+ return (parseAPIResponseStream(RestMethod.class, doBioCatalogueGET(restMethodURL)));
+ }
+
+ public Search getBioCatalogueSearchData(String searchURL) throws Exception {
+ return (parseAPIResponseStream(Search.class, doBioCatalogueGET(searchURL)));
+ }
+
+ public Tag getBioCatalogueTag(String searchByTagURL) throws Exception {
+ return (parseAPIResponseStream(Tag.class, doBioCatalogueGET(searchByTagURL)));
+ }
+
+ public Tags getBioCatalogueTags(String tagsURL) throws Exception {
+ return (parseAPIResponseStream(Tags.class, doBioCatalogueGET(tagsURL)));
+ }
+
+
+ public ResourceLink getBioCatalogueResource(Class<? extends ResourceLink> classOfResourceToFetch, String resourceURL) throws Exception {
+ return (parseAPIResponseStream(classOfResourceToFetch, doBioCatalogueGET(resourceURL)));
+ }
+
+
+ public <T extends ResourceLink> Pair<CollectionCoreStatistics, List<T>> getListOfItemsFromResourceCollectionIndex(
+ Class<T> classOfCollectionOfRequiredReturnedObjects, BioCatalogueAPIRequest filteringRequest) throws Exception
+ {
+ ResourceLink matchingItems = null;
+ if (filteringRequest.getRequestType() == BioCatalogueAPIRequest.TYPE.GET) {
+ matchingItems = parseAPIResponseStream(classOfCollectionOfRequiredReturnedObjects, doBioCatalogueGET(filteringRequest.getURL()));
+ }
+ else {
+ matchingItems = parseAPIResponseStream(classOfCollectionOfRequiredReturnedObjects,
+ doBioCataloguePOST_SendJSON_AcceptXML(filteringRequest.getURL(), filteringRequest.getData()));
+ }
+
+ CollectionCoreStatistics statistics = null;
+
+ List<T> matchingItemList = new ArrayList<T>();
+
+ // SOAP Operations
+ if (classOfCollectionOfRequiredReturnedObjects.equals(SoapOperations.class)) {
+ SoapOperations soapOperations = (SoapOperations)matchingItems;
+ matchingItemList.addAll((Collection<? extends T>)(soapOperations.getResults().getSoapOperationList()));
+ statistics = soapOperations.getStatistics();
+ }
+
+ // REST Methods
+ else if (classOfCollectionOfRequiredReturnedObjects.equals(RestMethods.class)) {
+ RestMethods restMethods = (RestMethods)matchingItems;
+ matchingItemList.addAll((Collection<? extends T>)(restMethods.getResults().getRestMethodList()));
+ statistics = restMethods.getStatistics();
+ }
+
+ // Services
+ else if (classOfCollectionOfRequiredReturnedObjects.equals(Services.class)) {
+ Services services = (Services)matchingItems;
+ matchingItemList.addAll((Collection<? extends T>)(services.getResults().getServiceList()));
+ statistics = services.getStatistics();
+ }
+
+ // Service Providers
+ else if (classOfCollectionOfRequiredReturnedObjects.equals(ServiceProviders.class)) {
+ ServiceProviders serviceProviders = (ServiceProviders)matchingItems;
+ matchingItemList.addAll((Collection<? extends T>)(serviceProviders.getResults().getServiceProviderList()));
+ statistics = serviceProviders.getStatistics();
+ }
+
+ // Users
+ else if (classOfCollectionOfRequiredReturnedObjects.equals(Users.class)) {
+ Users users = (Users)matchingItems;
+ matchingItemList.addAll((Collection<? extends T>)(users.getResults().getUserList()));
+ statistics = users.getStatistics();
+ }
+
+ // no such option - error
+ else {
+ return null;
+ }
+
+ return new Pair<CollectionCoreStatistics, List<T>>(statistics, matchingItemList);
+ }
+
+
+
+
+ /**
+ * @param wsdlLocation
+ * @param operationName
+ * @return SoapOperation instance or <code>null</code> if nothing was found (or error occurred).
+ * @throws Exception
+ */
+ public SoapOperation lookupSoapOperation(SoapOperationIdentity soapOperationDetails) throws Exception
+ {
+ // first of all check for any problems with input data
+ if (soapOperationDetails == null || soapOperationDetails.hasError() ||
+ soapOperationDetails.getWsdlLocation() == null || soapOperationDetails.getWsdlLocation().length() == 0 ||
+ soapOperationDetails.getOperationName() == null || soapOperationDetails.getOperationName().length() == 0)
+ {
+ // something's not right - return null
+ return (null);
+ }
+
+ String lookupURL = Util.appendURLParameter(API_LOOKUP_URL, API_LOOKUP_WSDL_LOCATION_PARAMETER, soapOperationDetails.getWsdlLocation());
+ lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_OPERATION_NAME_PARAMETER, soapOperationDetails.getOperationName());
+
+ ServerResponseStream lookupResponse = doBioCatalogueGET(lookupURL);
+ if (lookupResponse.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+ return null;
+ }
+ return (parseAPIResponseStream(SoapOperation.class, lookupResponse));
+ }
+
+
+ public <T extends ResourceLink> T lookupSoapOperationPort(Class<T> requiredResultClass, SoapOperationPortIdentity portDetails) throws Exception
+ {
+ // first of all check for any problems with port details
+ if (portDetails == null || portDetails.hasError() ||
+ portDetails.getWsdlLocation() == null || portDetails.getWsdlLocation().length() == 0 ||
+ portDetails.getOperationName() == null || portDetails.getOperationName().length() == 0 ||
+ portDetails.getPortName() == null || portDetails.getPortName().length() == 0)
+ {
+ // something's not right - return null
+ return (null);
+ }
+
+ // now check that specified class matches the port type
+ if (portDetails.isInput() && !requiredResultClass.equals(SoapInput.class) ||
+ !portDetails.isInput() && !requiredResultClass.equals(SoapOutput.class))
+ {
+ return (null);
+ }
+
+ String lookupURL = Util.appendURLParameter(API_LOOKUP_URL, API_LOOKUP_WSDL_LOCATION_PARAMETER, portDetails.getWsdlLocation());
+ lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_OPERATION_NAME_PARAMETER, portDetails.getOperationName());
+ if (portDetails.isInput()) {
+ lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_SOAP_INPUT_NAME_PARAMETER, portDetails.getPortName());
+ }
+ else {
+ lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_SOAP_OUTPUT_NAME_PARAMETER, portDetails.getPortName());
+ }
+
+ ServerResponseStream lookupResponse = doBioCatalogueGET(lookupURL);
+ if (lookupResponse.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+ return null;
+ }
+ return (parseAPIResponseStream(requiredResultClass, lookupResponse));
+ }
+
+
+ public Service lookupParentService(SoapOperationIdentity soapOperationDetails) throws Exception
+ {
+ SoapOperation soapOperation = this.lookupSoapOperation(soapOperationDetails);
+ if (soapOperation != null) {
+ return (getBioCatalogueService(soapOperation.getAncestors().getService().getHref()));
+ }
+ else {
+ // lookup didn't find the SOAP operation or there
+ // was some problem with the input data
+ return (null);
+ }
+ }
+
+
+ public Service lookupParentServiceMonitoringData(SoapOperationIdentity soapOperationDetails) throws Exception
+ {
+ SoapOperation soapOperation = this.lookupSoapOperation(soapOperationDetails);
+ if (soapOperation != null) {
+ return (getBioCatalogueServiceMonitoringData(soapOperation.getAncestors().getService().getHref()));
+ }
+ else {
+ // lookup didn't find the SOAP operation or there
+ // was some problem with the input data
+ return (null);
+ }
+ }
+
+
+ // ************ METHODS FOR RETRIEVAL OF SPECIALISED OBJECT FROM THE API VIA LITE JSON ************
+
+ public BeansForJSONLiteAPI.ResourceIndex getBioCatalogueResourceLiteIndex(TYPE resourceType, String resourceIndexURL) throws Exception
+ {
+ ServerResponseStream response = doBioCatalogueGET_LITE_JSON(resourceIndexURL);
+
+ Gson gson = new Gson();
+ return (ResourceIndex)(gson.fromJson(new InputStreamReader(response.getResponseStream()), resourceType.getJsonLiteAPIBindingBeanClass()));
+ }
+
+
+ public BeansForJSONLiteAPI.ResourceIndex postBioCatalogueResourceLiteIndex(TYPE resourceType, String resourceIndexURL, String postData) throws Exception
+ {
+ ServerResponseStream response = doBioCataloguePOST_SendJSON_AcceptLITEJSON(resourceIndexURL, postData);
+
+ Gson gson = new Gson();
+ return (ResourceIndex)(gson.fromJson(new InputStreamReader(response.getResponseStream()), resourceType.getJsonLiteAPIBindingBeanClass()));
+ }
+
+
+ // ************ GENERIC API CONNECTIVITY METHODS ************
+
+ /**
+ * Generic method to issue GET requests to BioCatalogue server.
+ *
+ * This is a convenience method to be used instead of {@link BioCatalogueClient#doBioCatalogueGET_XML(String)}.
+ *
+ * @param strURL The URL on BioCatalogue to issue GET request to.
+ * @return TODO
+ * @throws Exception
+ */
+ public ServerResponseStream doBioCatalogueGET(String strURL) throws Exception {
+ return (doBioCatalogueGET_XML(strURL));
+ }
+
+ public ServerResponseStream doBioCatalogueGET_XML(String strURL) throws Exception {
+ return (doBioCatalogueGET(strURL, XML_MIME_TYPE, XML_DATA_FORMAT));
+ }
+
+ public ServerResponseStream doBioCatalogueGET_JSON(String strURL) throws Exception {
+ return (doBioCatalogueGET(strURL, JSON_MIME_TYPE, JSON_DATA_FORMAT));
+ }
+
+ public ServerResponseStream doBioCatalogueGET_LITE_JSON(String strURL) throws Exception {
+ return (doBioCatalogueGET(strURL, LITE_JSON_MIME_TYPE, LITE_JSON_DATA_FORMAT));
+ }
+
+
+ public ServerResponseStream doBioCatalogueGET(String strURL, String ACCEPT_HEADER, String REQUESTED_DATA_FORMAT) throws Exception
+ {
+ // TODO - HACK to speed up processing append .xml / .json / .bljson to all URLs to avoid LinkedData content negotiation
+ strURL = Util.appendStringBeforeParametersOfURL(strURL, REQUESTED_DATA_FORMAT);
+
+ // open server connection using provided URL (with no further modifications to it)
+ URL url = new URL(strURL);
+
+ Calendar requestStartedAt = Calendar.getInstance();
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+ conn.setRequestProperty("Accept", ACCEPT_HEADER);
+
+// if(LOGGED_IN) {
+// // if the user has "logged in", also add authentication details
+// conn.setRequestProperty("Authorization", "Basic " + AUTH_STRING);
+// }
+
+ // fetch server's response
+ ServerResponseStream serverResponse = doBioCatalogueReceiveServerResponse(conn, strURL, true);
+
+ if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING) {
+ logAPIOperation(requestStartedAt, "GET", serverResponse);
+ }
+ return (serverResponse);
+ }
+
+
+
+ public ServerResponseStream doBioCataloguePOST_SendJSON_AcceptXML(String strURL, String strDataBody) throws Exception {
+ return (doBioCataloguePOST(strURL, strDataBody, JSON_MIME_TYPE, XML_MIME_TYPE, XML_DATA_FORMAT));
+ }
+
+ public ServerResponseStream doBioCataloguePOST_SendJSON_AcceptLITEJSON(String strURL, String strDataBody) throws Exception {
+ return (doBioCataloguePOST(strURL, strDataBody, JSON_MIME_TYPE, LITE_JSON_MIME_TYPE, LITE_JSON_DATA_FORMAT));
+ }
+
+
+ /**
+ * Generic method to execute POST requests to BioCatalogue server.
+ *
+ * @param strURL The URL on BioCatalogue to POST to.
+ * @param strDataBody Body of the message to be POSTed to <code>strURL</code>.
+ * @return An object containing server's response body as an InputStream and
+ * a response code.
+ * @param CONTENT_TYPE_HEADER MIME type of the sent data.
+ * @param ACCEPT_HEADER MIME type of the data to be received.
+ * @param REQUESTED_DATA_FORMAT
+ * @throws Exception
+ */
+ public ServerResponseStream doBioCataloguePOST(String strURL, String strDataBody, String CONTENT_TYPE_HEADER,
+ String ACCEPT_HEADER, String REQUESTED_DATA_FORMAT) throws Exception
+ {
+ // TODO - HACK to speed up processing append .xml / .json / .bljson to all URLs to avoid LinkedData content negotiation
+ strURL = Util.appendStringBeforeParametersOfURL(strURL, REQUESTED_DATA_FORMAT);
+
+ // open server connection using provided URL (with no further modifications to it)
+ URL url = new URL (strURL);
+
+ Calendar requestStartedAt = Calendar.getInstance();
+ HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
+ urlConn.setRequestMethod("POST");
+ urlConn.setDoOutput(true);
+ urlConn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+ urlConn.setRequestProperty("Content-Type", CONTENT_TYPE_HEADER);
+ urlConn.setRequestProperty("Accept", ACCEPT_HEADER);
+
+ // prepare and POST XML data
+ OutputStreamWriter out = new OutputStreamWriter(urlConn.getOutputStream());
+ out.write(strDataBody);
+ out.close();
+
+
+ // fetch server's response
+ ServerResponseStream serverResponse = doBioCatalogueReceiveServerResponse(urlConn, strURL, false);
+
+ if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING) {
+ logAPIOperation(requestStartedAt, "POST", serverResponse);
+ }
+ return (serverResponse);
+ }
+
+
+ /**
+ * Generic method to execute DELETE requests to myExperiment server.
+ * This is only to be called when a user is logged in.
+ *
+ * @param strURL The URL on myExperiment to direct DELETE request to.
+ * @return An object containing XML Document with server's response body and
+ * a response code. Response body XML document might be null if there
+ * was an error or the user wasn't authorised to perform a certain action.
+ * Response code will always be set.
+ * @throws Exception
+ */
+ /*public ServerResponse doMyExperimentDELETE(String strURL) throws Exception
+ {
+ // open server connection using provided URL (with no modifications to it)
+ URL url = new URL(strURL);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+
+ // "tune" the connection
+ conn.setRequestMethod("DELETE");
+ conn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+ conn.setRequestProperty("Authorization", "Basic " + AUTH_STRING);
+
+ // check server's response
+ return (doMyExperimentReceiveServerResponse(conn, strURL, true));
+ }*/
+
+
+ /**
+ * A common method for retrieving BioCatalogue server's response for both
+ * GET and POST requests.
+ *
+ * @param conn Instance of the established URL connection to poll for server's response.
+ * @param strURL The URL on BioCatalogue with which the connection is established.
+ * @param bIsGetRequest Flag for identifying type of the request. True when the current
+ * connection executes GET request; false when it executes a POST / DELETE request.
+ * @return TODO
+ */
+ @SuppressWarnings("unchecked")
+ private ServerResponseStream doBioCatalogueReceiveServerResponse(HttpURLConnection conn, String strURL, boolean bIsGETRequest) throws Exception
+ {
+ int iResponseCode = conn.getResponseCode();
+
+ switch (iResponseCode)
+ {
+ case HttpURLConnection.HTTP_OK:
+ // regular operation path - simply return the reference to the data input stream
+ return (new ServerResponseStream(iResponseCode, conn.getInputStream(), strURL));
+
+ case HttpURLConnection.HTTP_BAD_REQUEST:
+ // this was a bad XML request - need full XML response to retrieve the error message from it;
+ // Java throws IOException if getInputStream() is used when non HTTP_OK response code was received -
+ // hence can use getErrorStream() straight away to fetch the error document
+ return (new ServerResponseStream(iResponseCode, conn.getErrorStream(), strURL));
+
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ // this content is not authorised for current user
+ return (new ServerResponseStream(iResponseCode, null, strURL));
+
+ case HttpURLConnection.HTTP_NOT_FOUND:
+ // nothing was found at the provided URL
+ return (new ServerResponseStream(iResponseCode, conn.getErrorStream(), strURL));
+
+ default:
+ // unexpected response code - raise an exception
+ throw new IOException("Received unexpected HTTP response code (" + iResponseCode + ") while " +
+ (bIsGETRequest ? "fetching data at " : "posting data to ") + strURL);
+ }
+ }
+
+
+ /**
+ * This method is here to make sure that *all* parsing of received input stream data
+ * from the API is parsed ("bound") into Java objects in a central place - so it's
+ * possible to measure performance of XmlBeans for various inputs.
+ *
+ * NB! There is a serious limitation in Java's generics. Generic methods cannot
+ * access any of the static context of the classes of type parameters, because
+ * it wasn't designed for this. The only purpose of type parameters is compile-time
+ * type-checking.
+ * This means that even though all classes that could potentially be supplied as a
+ * type-parameter would have certain static functionality, it's not possible to access
+ * that through using the type-parameter like it's done in normal polymorhic situations.
+ * Therefore, some switching based on the class of the type-parameter for this method is
+ * done...
+ *
+ * @param <T>
+ * @param classOfRequiredReturnedObject Class of the object that the caller expects to receive
+ * after parsing provided server's response. For example,
+ * a call to /tags.xml return the <pre>[tags]...[/tags]</pre>
+ * document. <code>TagsDocument</code> should be used to access
+ * its static factory and parse the input stream - the return
+ * value will have type <code>Tags</code> -- <code>Tags.class</code>
+ * is the required input value for this parameter in this situation then.
+ * @param serverResponse This object should contain the input stream obtained from the API in return
+ * to the call on some URL.
+ * @return InputStream data parsed into the Java object of the supplied type [T].
+ * @throws Exception
+ */
+ @SuppressWarnings("unchecked")
+ private <T extends ResourceLink> T parseAPIResponseStream(Class<T> classOfRequiredReturnedObject, ServerResponseStream serverResponse) throws Exception
+ {
+ T parsedObject = null;
+ InputStream xmlInputStream = serverResponse.getResponseStream();
+
+ // choose a factory to parse the response and perform parsing
+ Calendar parsingStartedAt = Calendar.getInstance();
+ if (classOfRequiredReturnedObject.equals(Annotations.class)) {
+ parsedObject = (T)AnnotationsDocument.Factory.parse(xmlInputStream).getAnnotations();
+ }
+ else if (classOfRequiredReturnedObject.equals(Filters.class)) {
+ parsedObject = (T)FiltersDocument.Factory.parse(xmlInputStream).getFilters();
+ }
+ else if (classOfRequiredReturnedObject.equals(RestMethods.class)) {
+ parsedObject = (T)RestMethodsDocument.Factory.parse(xmlInputStream).getRestMethods();
+ }
+ else if (classOfRequiredReturnedObject.equals(RestMethod.class)) {
+ parsedObject = (T)RestMethodDocument.Factory.parse(xmlInputStream).getRestMethod();
+ }
+ else if (classOfRequiredReturnedObject.equals(Search.class)) {
+ parsedObject = (T)SearchDocument.Factory.parse(xmlInputStream).getSearch();
+ }
+ else if (classOfRequiredReturnedObject.equals(Services.class)) {
+ parsedObject = (T)ServicesDocument.Factory.parse(xmlInputStream).getServices();
+ }
+ else if (classOfRequiredReturnedObject.equals(Service.class)) {
+ parsedObject = (T)ServiceDocument.Factory.parse(xmlInputStream).getService();
+ }
+ else if (classOfRequiredReturnedObject.equals(ServiceProviders.class)) {
+ parsedObject = (T)ServiceProvidersDocument.Factory.parse(xmlInputStream).getServiceProviders();
+ }
+ else if (classOfRequiredReturnedObject.equals(ServiceProvider.class)) {
+ parsedObject = (T)ServiceProviderDocument.Factory.parse(xmlInputStream).getServiceProvider();
+ }
+ else if (classOfRequiredReturnedObject.equals(SoapOperations.class)) {
+ parsedObject = (T)SoapOperationsDocument.Factory.parse(xmlInputStream).getSoapOperations();
+ }
+ else if (classOfRequiredReturnedObject.equals(SoapOperation.class)) {
+ parsedObject = (T)SoapOperationDocument.Factory.parse(xmlInputStream).getSoapOperation();
+ }
+ else if (classOfRequiredReturnedObject.equals(SoapService.class)) {
+ parsedObject = (T)SoapServiceDocument.Factory.parse(xmlInputStream).getSoapService();
+ }
+ else if (classOfRequiredReturnedObject.equals(SoapInput.class)) {
+ parsedObject = (T)SoapInputDocument.Factory.parse(xmlInputStream).getSoapInput();
+ }
+ else if (classOfRequiredReturnedObject.equals(SoapOutput.class)) {
+ parsedObject = (T)SoapOutputDocument.Factory.parse(xmlInputStream).getSoapOutput();
+ }
+ else if (classOfRequiredReturnedObject.equals(Tags.class)) {
+ parsedObject = (T)TagsDocument.Factory.parse(xmlInputStream).getTags();
+ }
+ else if (classOfRequiredReturnedObject.equals(Tag.class)) {
+ parsedObject = (T)TagDocument.Factory.parse(xmlInputStream).getTag();
+ }
+ else if (classOfRequiredReturnedObject.equals(Users.class)) {
+ parsedObject = (T)UsersDocument.Factory.parse(xmlInputStream).getUsers();
+ }
+ else if (classOfRequiredReturnedObject.equals(User.class)) {
+ parsedObject = (T)UserDocument.Factory.parse(xmlInputStream).getUser();
+ }
+
+
+ // log the operation if necessary
+ if (BioCataloguePluginConstants.PERFORM_API_XML_DATA_BINDING_TIME_LOGGING) {
+ logAPIOperation(parsingStartedAt, null, serverResponse);
+ }
+
+ return (parsedObject);
+ }
+
+
+ // ************ HELPERS ************
+
+ public static DateFormat getDateFormatter() {
+ return(BioCatalogueClient.DATE_FORMATTER);
+ }
+
+ public static DateFormat getShortDateFormatter() {
+ return(BioCatalogueClient.SHORT_DATE_FORMATTER);
+ }
+
+
+ /**
+ * This is a helper to facilitate performance monitoring of the API usage.
+ *
+ * @param opearationStartedAt Instance of Calendar initialised with the date/time value of
+ * when the logged operation was started.
+ * @param requestType "GET" or "POST" to indicate that this was the actual URL connection with the BioCatalogue server
+ * to fetch some data; <code>null</code> to indicate an xml-binding operation using XmlBeans.
+ * @param serverResponse Will be used to extract the request URL.
+ */
+ private void logAPIOperation(Calendar opearationStartedAt, String requestType, ServerResponseStream serverResponse)
+ {
+ // just in case check that the writer was initialised
+ if (pwAPILogWriter != null) {
+ pwAPILogWriter.println(API_LOGGING_TIMESTAMP_FORMATTER.format(opearationStartedAt.getTime()) + ", " +
+ (System.currentTimeMillis() - opearationStartedAt.getTimeInMillis()) + ", " +
+ (requestType == null ? "xml_parsing" : requestType) + ", " +
+ serverResponse.getRequestURL());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
new file mode 100644
index 0000000..d79235d
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
@@ -0,0 +1,40 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import net.sf.taverna.biocatalogue.model.Util;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public abstract class ServerResponse
+{
+ // this code is to be used when a local failure is encountered and the
+ // server response is a blank / invalid one
+ public static int LOCAL_FAILURE_CODE = -1;
+
+ // server response code - in theory should correspond to HTTP response codes
+ private int iResponseCode;
+
+ // URL that was used to make the request
+ private String requestURL;
+
+
+ public ServerResponse() {
+ // do nothing - empty constructor
+ }
+
+ public ServerResponse(int responseCode, String requestURL) {
+ super();
+ this.iResponseCode = responseCode;
+ this.requestURL = Util.urlDecodeQuery(requestURL);
+ }
+
+
+ public int getResponseCode() {
+ return (this.iResponseCode);
+ }
+
+ public String getRequestURL() {
+ return requestURL;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
new file mode 100644
index 0000000..d4ebb56
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
@@ -0,0 +1,30 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import java.io.InputStream;
+
+/**
+ * This class is a custom version of ServerResponse which contains the
+ * InputStream with the the actual server response data.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class ServerResponseStream extends ServerResponse
+{
+ private InputStream responseStream;
+
+ public ServerResponseStream(int responseCode, InputStream serverResponseStream, String requestURL)
+ {
+ super(responseCode, requestURL);
+ this.setResponseStream(serverResponseStream);
+ }
+
+ public void setResponseStream(InputStream responseStream)
+ {
+ this.responseStream = responseStream;
+ }
+
+ public InputStream getResponseStream()
+ {
+ return responseStream;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
new file mode 100644
index 0000000..0c4018a
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
@@ -0,0 +1,221 @@
+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();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
new file mode 100644
index 0000000..fa43632
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
@@ -0,0 +1,490 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Tag;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsRenderer;
+
+
+/**
+ * Class to hold settings for search instance. Objects of this type will
+ * be used to re-run a search instance at a later time -- or to apply
+ * filtering onto a previously executed search.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class SearchInstance implements Comparable<SearchInstance>, Serializable
+{
+ private static final long serialVersionUID = -5236966374301885370L;
+
+ // CONSTANTS
+ public static enum TYPE
+ {
+ QuerySearch(ResourceManager.getImageIcon(ResourceManager.SEARCH_ICON)),
+ TagSearch(ResourceManager.getImageIcon(ResourceManager.TAG_ICON)),
+ Filtering(ResourceManager.getImageIcon(ResourceManager.FILTER_ICON));
+
+ private Icon icon;
+
+ /**
+ * @param icon Icon to represent search instances in different listings
+ * - for example in search history.
+ */
+ TYPE(Icon icon) {
+ this.icon = icon;
+ }
+
+ /**
+ * @return An icon that is most suitable to display search instance of this type in a UI component.
+ */
+ public Icon getIcon() {
+ return this.icon;
+ }
+ }
+
+
+
+ // SEARCH SETTINGS - for either search by query or search by tag
+ private TYPE searchType;
+ private final TYPE serviceFilteringBasedOn; // service filtering may be based on {@link TYPE.QuerySearch} or {@link TYPE.TagSearch}
+ private final Resource.TYPE resourceTypeToSearchFor;
+
+ private final String searchString;
+ private final List<Tag> searchTags;
+
+
+ // SERVICE FILTERING settings
+ private ServiceFilteringSettings filteringSettings;
+
+ // SEARCH RESULTS
+ private transient SearchResults searchResults; // don't want to store search results when serialising...
+
+
+
+ /**
+ * Constructs a query search instance for finding instance of a specific resource type.
+ *
+ * @param searchString
+ * @param resourceTypeToSearchFor
+ */
+ public SearchInstance(String searchString, Resource.TYPE resourceTypeToSearchFor)
+ {
+ this.searchType = TYPE.QuerySearch;
+ this.serviceFilteringBasedOn = null;
+
+ this.resourceTypeToSearchFor = resourceTypeToSearchFor;
+
+ this.searchString = searchString;
+ this.searchTags = null;
+ }
+
+
+
+ /**
+ * Constructing a search instance for finding instance of a specific resource type by a single tag.
+ *
+ * @param searchTag
+ * @param resourceTypeToSearchFor
+ */
+ public SearchInstance(Tag searchTag, Resource.TYPE resourceTypeToSearchFor)
+ {
+ this.searchType = TYPE.TagSearch;
+ this.serviceFilteringBasedOn = null;
+
+ this.resourceTypeToSearchFor = resourceTypeToSearchFor;
+
+ this.searchTags = Collections.singletonList(searchTag);
+ this.searchString = null;
+ }
+
+
+ /**
+ * Constructing a search instance for finding instance of a specific resource type by a list of tags.
+ *
+ * @param searchTags
+ * @param resourceTypeToSearchFor
+ */
+ public SearchInstance(List<Tag> searchTags, Resource.TYPE resourceTypeToSearchFor)
+ {
+ this.searchType = TYPE.TagSearch;
+ this.serviceFilteringBasedOn = null;
+
+ this.resourceTypeToSearchFor = resourceTypeToSearchFor;
+
+ this.searchTags = searchTags;
+ this.searchString = null;
+ }
+
+
+
+ /**
+ * Constructing service filtering search instance.
+ *
+ * @param si SearchInstance to base the current on.
+ * Can be either {@link TYPE#TagSearch} or {@link TYPE#QuerySearch} type of SearchInstance.
+ * @param filteringSettings Filtering settings associated with this search instance.
+ */
+ public SearchInstance(SearchInstance si, ServiceFilteringSettings filteringSettings) throws IllegalArgumentException
+ {
+ if (!si.isTagSearch() && !si.isQuerySearch()) {
+ throw new IllegalArgumentException("Cannot create Service Filtering search instance - " +
+ "supplied base search instance must be either QuerySearch or TagSearch");
+ }
+
+ this.searchType = TYPE.Filtering;
+ this.serviceFilteringBasedOn = si.searchType;
+
+ this.resourceTypeToSearchFor = si.resourceTypeToSearchFor;
+
+ // this search instance inherits search term (i.e. search query or the tag) from the supplied search instance
+ this.searchString = si.isQuerySearch() ? si.searchString : null;
+ this.searchTags = si.isTagSearch() ? si.searchTags : null;
+
+ // also, store the filtering settings that are to be applied to the newly
+ // created search instance
+ this.filteringSettings = filteringSettings;
+ }
+
+
+ /**
+ * Determines whether the two search instances are identical.
+ */
+ // TODO - fix the equals() method
+ public boolean equals(Object other)
+ {
+ if (other instanceof SearchInstance)
+ {
+ SearchInstance s = (SearchInstance)other;
+
+ boolean bSearchTypesMatch = (this.searchType == s.getSearchType());
+ if (bSearchTypesMatch) {
+ switch (this.searchType) {
+ case QuerySearch: bSearchTypesMatch = this.searchString.equals(s.getSearchString()); break;
+
+ case TagSearch: bSearchTypesMatch = this.searchTags.equals(s.getSearchTags()); break;
+
+ case Filtering: bSearchTypesMatch = this.serviceFilteringBasedOn == s.getServiceFilteringBasedOn();
+ if (bSearchTypesMatch) {
+ if (this.serviceFilteringBasedOn == TYPE.QuerySearch) {
+ bSearchTypesMatch = this.searchString.equals(s.getSearchString());
+ }
+ else {
+ bSearchTypesMatch = this.searchTags.equals(s.getSearchTags());
+ }
+ }
+ if (bSearchTypesMatch) {
+ if (this.filteringSettings != null) {
+ bSearchTypesMatch = this.filteringSettings.equals(s.getFilteringSettings());
+ }
+ else if (s.filteringSettings != null) {
+ // other isn't null, this one is - so 'false'
+ bSearchTypesMatch = false;
+ }
+ else {
+ // both could be null
+ bSearchTypesMatch = (this.filteringSettings == s.getFilteringSettings());
+ }
+ }
+ break;
+ default: bSearchTypesMatch = false;
+ }
+ }
+
+ return (bSearchTypesMatch &&
+ /* TODO re-enable this when limits are implemented -- this.iResultCountLimit == s.getResultCountLimit() && */
+ this.resourceTypeToSearchFor == s.getResourceTypeToSearchFor());
+ }
+ else
+ return (false);
+ }
+
+
+ public int compareTo(SearchInstance other)
+ {
+ if (this.equals(other)) return(0);
+ else
+ {
+ // this will return results in the descending order - which is
+ // fine, because the way this collection will be rendered will
+ // eventually traverse it from the rear end first; so results
+ // will be shown alphabetically
+ return (-1 * this.toString().compareTo(other.toString()));
+ }
+ }
+
+
+ /**
+ * See {@link SearchInstance#getDescriptionStringForSearchStatus(SearchInstance)}
+ */
+ public String getDescriptionStringForSearchStatus() {
+ return (getDescriptionStringForSearchStatus(this));
+ }
+
+
+ /**
+ * @param si {@link SearchInstance} for which the method is executed.
+ * @return String that can be used as a description of the provided {@link SearchInstance}
+ * in the search status label. Returned strings may look like: <br/>
+ * - <code>empty search string</code><br/>
+ * - <code>query "[search_query]"</code><br/>
+ * - <code>tag "[search_tag]"</code><br/>
+ * - <code>tags "[tag1]", "[tag2]", "[tag3]"</code><br/>
+ * - <code>query "[search_query]" and X filter(s)</code><br/>
+ * - <code>tag "[search_tag]" and X filter(s)</code><br/>
+ * - <code>tags "[tag1]", "[tag2]", "[tag3]" and X filter(s)</code><br/>
+ */
+ public static String getDescriptionStringForSearchStatus(SearchInstance si)
+ {
+ switch (si.searchType)
+ {
+ case QuerySearch: String searchQuery = si.getSearchTerm();
+ return (searchQuery.length() == 0 ?
+ "empty search string" :
+ "query " + si.getSearchTerm());
+
+ case TagSearch: return (Util.pluraliseNoun("tag", si.getSearchTags().size()) + " " + si.getSearchTerm());
+
+ case Filtering: int filterNumber = si.getFilteringSettings().getNumberOfFilteringCriteria();
+
+ SearchInstance tempBaseSI = si.deepCopy();
+ tempBaseSI.searchType = si.getServiceFilteringBasedOn();
+ return getDescriptionStringForSearchStatus(tempBaseSI) + " and " + filterNumber + " " + Util.pluraliseNoun("filter", filterNumber);
+
+ default: return ("unexpected type of search");
+ }
+ }
+
+
+ public String toString()
+ {
+ String out = "<html>";
+
+ if (this.isQuerySearch() || this.isTagSearch()) {
+ out += (this.isTagSearch() ? "Tag s" : "S") + "earch: '" + getSearchTerm() + "' [" + this.detailsAsString() + "]";
+ }
+ else if (this.isServiceFilteringSearch()) {
+ out += "Filter:<br>" +
+ (getSearchTerm().length() > 0 ? ("- based on " + (this.isQuerySearch() ? "term" : "tag") + " '" + getSearchTerm() + "'<br>") : "") +
+ "- scope: " + detailsAsString() + "<br>" +
+ "- " + this.filteringSettings.detailsAsString();
+ }
+
+ out += "</html>";
+
+ return (out);
+ }
+
+
+ /**
+ * @return A string representation of search settings held in this object;
+ * actual search value (string/tag) are ignored - this only affects
+ * types to search and the number of objects to fetch.
+ */
+ public String detailsAsString()
+ {
+ // include the name of the resource type collection that is to be / was searched for
+ String str = this.getResourceTypeToSearchFor().getCollectionName();
+
+ // add the rest to the string representation of the search instance
+ str = str /* TODO re-enable when limits are implemented -- "; limit: " + this.iResultCountLimit +*/;
+
+ return (str);
+ }
+
+
+
+ // ***** Getters for all fields *****
+
+ /**
+ * @return Type of this search instance.
+ */
+ public TYPE getSearchType() {
+ return (this.searchType);
+ }
+
+
+ /**
+ * @return True if this search settings instance describes a search by tag.
+ */
+ public boolean isTagSearch() {
+ return (this.searchType == TYPE.TagSearch);
+ }
+
+
+ /**
+ * @return True if this search settings instance describes a search by query.
+ */
+ public boolean isQuerySearch() {
+ return (this.searchType == TYPE.QuerySearch);
+ }
+
+
+ /**
+ * @return True if this search settings instance describes service filtering operation.
+ */
+ public boolean isServiceFilteringSearch() {
+ return (this.searchType == TYPE.Filtering);
+ }
+
+
+ /**
+ * Allows to test which type of search this filtering operation is based on -- any filtering
+ * operation can be:
+ * <li>derived from an initial search by tag(s) or by free-text query</li>
+ * <li>or can be just a standalone filtering operation, where filtering criteria are
+ * applied to all resources of the specified type, without prior search.</li>
+ *
+ * @return Value {@link TYPE#QuerySearch} or {@link TYPE#TagSearch} if this filtering operation has a known "parent",<br/>
+ * <code>null</code> if this is a proper search (not a filtering!) operation, or
+ * if this filtering operation was not based on any search.
+ */
+ public TYPE getServiceFilteringBasedOn() {
+ return serviceFilteringBasedOn;
+ }
+
+
+ public Resource.TYPE getResourceTypeToSearchFor() {
+ return this.resourceTypeToSearchFor;
+ }
+
+
+ /**
+ * @return Search string; only valid when SearchSettings object holds data about a search by query, not a tag search.
+ */
+ public String getSearchString() {
+ return searchString;
+ }
+
+ public List<Tag> getSearchTags() {
+ return searchTags;
+ }
+
+ /**
+ * This method is to be used when the type of search is not checked - in
+ * case of query search the method returns the search string, otherwise
+ * the tag(s) that is to be searched.
+ *
+ * @return The value will be returned in double quotes.
+ */
+ public String getSearchTerm()
+ {
+ if (this.searchType == TYPE.QuerySearch || this.serviceFilteringBasedOn == TYPE.QuerySearch) {
+ return (this.searchString.length() == 0 ?
+ "" :
+ "\"" + this.searchString + "\"");
+ }
+ else {
+ List<String> tagDisplayNames = new ArrayList<String>();
+ for (Tag t : this.searchTags) {
+ tagDisplayNames.add(t.getTagDisplayName());
+ }
+ return (Util.join(tagDisplayNames, "\"", "\"", ", "));
+ }
+ }
+
+
+ public ServiceFilteringSettings getFilteringSettings() {
+ return filteringSettings;
+ }
+ public void setFilteringSettings(ServiceFilteringSettings filteringSettings) {
+ this.filteringSettings = filteringSettings;
+ }
+
+
+ public SearchResults getSearchResults() {
+ return searchResults;
+ }
+ public void setSearchResults(SearchResults searchResults) {
+ this.searchResults = searchResults;
+ }
+
+ /**
+ * @return True if search results are available;
+ * False if no search results are available - probably search hasn't been carried out yet.
+ */
+ public boolean hasSearchResults() {
+ return (searchResults != null);
+ }
+
+ /**
+ * @return True if this is a new search; false otherwise.
+ * (Search is currently treated as new if there are no search results available yet.)
+ */
+ public boolean isNewSearch() {
+ return (!hasSearchResults());
+ }
+
+ /**
+ * Removes any previous search results; after execution of
+ * this method this search instance is treated as "new search".
+ */
+ public void clearSearchResults() {
+ this.searchResults = null;
+ }
+
+
+
+
+ // *** Methods that call SearchEngine in order to start new / resume result fetching for a previous search ***
+ //
+ // They are used to relay external calls to these methods to the underlying instance
+ // of SearchEngine which will perform the actual search operations for this search instance.
+
+ /**
+ * @param activeSearchInstanceTracker Tracker of current search instances for different resource types -
+ * aids in early termination of older searches.
+ * @param doneSignal Means of notifying the parentSeachThread of completing the requested search operation.
+ * The parent thread will block until doneSignal is activated.
+ * @param renderer {@link SearchResultsRenderer} that will render results of this search.
+ */
+ public void startNewSearch(SearchInstanceTracker activeSearchInstanceTracker,
+ CountDownLatch doneSignal, SearchResultsRenderer renderer)
+ {
+ new SearchEngine(this, activeSearchInstanceTracker, doneSignal, renderer).startNewSearch();
+ }
+
+
+ /**
+ * @param activeSearchInstanceTracker Tracker of current search instances for different resource types -
+ * aids in early termination of older searches.
+ * @param doneSignal Means of notifying the parentSeachThread of completing the requested search operation.
+ * The parent thread will block until doneSignal is activated.
+ * @param renderer {@link SearchResultsRenderer} that will render results of this search.
+ * @param resultPageNumber
+ */
+ public void fetchMoreResults(SearchInstanceTracker activeSearchInstanceTracker,
+ CountDownLatch doneSignal, SearchResultsRenderer renderer, int resultPageNumber)
+ {
+ new SearchEngine(this, activeSearchInstanceTracker, doneSignal, renderer).fetchMoreResults(resultPageNumber);
+ }
+
+
+
+
+ /**
+ * Used in the plugin, for example, to transfer search results from Search tab to
+ * Filtering tab. This way both tabs will remain completely independent.
+ *
+ * @return Deep copy of this SearchInstance object. If deep copying doesn't succeed,
+ * <code>null</code> is returned.
+ */
+ public SearchInstance deepCopy() {
+ return (SearchInstance)Util.deepCopy(this);
+ }
+
+ public boolean isEmptySearch() {
+ return ((searchString == null) || searchString.isEmpty()) &&
+ ((searchTags == null) || searchTags.isEmpty()) &&
+ ((filteringSettings == null) || (filteringSettings.getNumberOfFilteringCriteria() == 0));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
new file mode 100644
index 0000000..906ded1
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
@@ -0,0 +1,57 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+
+/**
+ * Implementations of this interface will keep track of
+ * current search instances for different resource types
+ * (under assumption that {@link SearchInstance} classes
+ * can only deal with one resource type per instance).
+ *
+ * In the BioCatalogue plugin it is one of the UI components
+ * that needs to keep track of active search instances. This
+ * interface helps to decouple the search engine model from the
+ * UI.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public interface SearchInstanceTracker
+{
+ /**
+ * Clears all records of previous search instances.
+ */
+ public void clearPreviousSearchInstances();
+
+ /**
+ * Registers an instance of {@link SearchInstance} class
+ * as a current one for a specific resource type.
+ *
+ * Repeated calls to this method with the same parameter
+ * should overwrite old values.
+ *
+ * @param searchType Resource type to associate the registered
+ * {@link SearchInstance} with.
+ */
+ public void registerSearchInstance(Resource.TYPE searchType, SearchInstance searchInstance);
+
+
+ /**
+ * Tests if provided {@link SearchInstance} is registered as the
+ * current one.
+ *
+ * @param searchType Resource type to perform the test for.
+ * @param searchInstance {@link SearchInstance} object that is expected to be
+ * the current search instance for the specified resource type.
+ * @return <code>true</code> - if the provided <code>searchInstance</code> is indeed
+ * currently registered as the active one for the given resouce type;<br/>
+ * <code>false</code> - otherwise.
+ */
+ public boolean isCurrentSearchInstance(Resource.TYPE searchType, SearchInstance searchInstance);
+
+
+ /**
+ * @param searchType
+ * @return Currently active {@link SearchInstance} object for the specified resource type.
+ */
+ public SearchInstance getCurrentSearchInstance(Resource.TYPE searchType);
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
new file mode 100644
index 0000000..cf5e7db
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
@@ -0,0 +1,70 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.sf.taverna.biocatalogue.model.Tag;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.ui.SearchOptionsPanel;
+
+/**
+ * Instances of this class can store the state of the
+ * {@link SearchOptionsPanel} / {@link TagSelectionDialog} in
+ * order to help instantiate {@link SearchInstance} objects.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class SearchOptions
+{
+ private SearchInstance preconfiguredSearchInstance;
+ private SearchInstance.TYPE searchType;
+ private String searchString;
+ private List<Tag> searchTags;
+ private List<TYPE> resourceTypesToSearchFor;
+
+ public SearchOptions(String searchString, List<TYPE> searchTypes) {
+ this.preconfiguredSearchInstance = null;
+ this.searchType = SearchInstance.TYPE.QuerySearch;
+ this.searchString = searchString;
+ this.searchTags = null;
+ this.resourceTypesToSearchFor = searchTypes;
+ }
+
+ public SearchOptions(List<Tag> searchTags, List<TYPE> searchTypes) {
+ this.preconfiguredSearchInstance = null;
+ this.searchType = SearchInstance.TYPE.TagSearch;
+ this.searchString = null;
+ this.searchTags = searchTags;
+ this.resourceTypesToSearchFor = searchTypes;
+ }
+
+ public SearchOptions(SearchInstance preconfiguredSearchInstance) {
+ this.preconfiguredSearchInstance = preconfiguredSearchInstance;
+ this.searchType = preconfiguredSearchInstance.getSearchType();
+ this.searchString = preconfiguredSearchInstance.getSearchString();
+ this.searchTags = preconfiguredSearchInstance.getSearchTags();
+ this.resourceTypesToSearchFor = Collections.singletonList(preconfiguredSearchInstance.getResourceTypeToSearchFor());
+ }
+
+
+ public SearchInstance getPreconfiguredSearchInstance() {
+ return preconfiguredSearchInstance;
+ }
+
+ public SearchInstance.TYPE getSearchType() {
+ return searchType;
+ }
+
+ public String getSearchString() {
+ return searchString;
+ }
+
+ public List<Tag> getSearchTags() {
+ return searchTags;
+ }
+
+ public List<TYPE> getResourceTypesToSearchFor() {
+ return resourceTypesToSearchFor;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
new file mode 100644
index 0000000..d18076c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
@@ -0,0 +1,214 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceLinkWithName;
+
+import org.apache.log4j.Logger;
+
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+
+
+/**
+ * Generic class for any kinds of search results.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class SearchResults implements Serializable
+{
+ private static final long serialVersionUID = 6994685875323246165L;
+
+ private transient Logger logger; // don't want to serialise the logger...
+
+ private final TYPE typeOfResourcesInTheResultSet;
+ private final int totalResultCount;
+
+ // Data store for found items
+ protected ArrayList<ResourceLink> foundItems;
+ private int fullyFetchedItemCount;
+
+
+
+ public SearchResults(TYPE typeOfResourcesInTheResultSet, BeansForJSONLiteAPI.ResourceIndex resourceIndex)
+ {
+ this.typeOfResourcesInTheResultSet = typeOfResourcesInTheResultSet;
+ this.totalResultCount = resourceIndex.getResources().length;
+ this.fullyFetchedItemCount = 0;
+
+ this.logger = Logger.getLogger(this.getClass());
+
+ initialiseSearchResultCollection(resourceIndex);
+ }
+
+
+ /**
+ * The collection of results is initialised to cater for the expected number of
+ * values - placeholder with just a name and URL for each of the expected result entries is stored.
+ *
+ * @param resourceIndex
+ */
+ protected void initialiseSearchResultCollection(ResourceIndex resourceIndex)
+ {
+ foundItems = new ArrayList<ResourceLink>();
+ foundItems.ensureCapacity(getTotalMatchingItemCount());
+
+ ResourceLinkWithName resourceLink = null;
+ for (int i = 0; i < getTotalMatchingItemCount(); i++) {
+ resourceLink = resourceIndex.getResources()[i];
+ this.foundItems.add(new LoadingResource(resourceLink.getURL(), resourceLink.getName()));
+ }
+ }
+
+
+ public synchronized void addSearchResults(List<ResourceLink> searchResultsData, int positionToStartAddingResults)
+ {
+ // only update a specific portion of results
+ for (int i = 0; i < searchResultsData.size(); i++) {
+ this.foundItems.set(i + positionToStartAddingResults, searchResultsData.get(i));
+ }
+
+ fullyFetchedItemCount += searchResultsData.size();
+ }
+
+
+ public TYPE getTypeOfResourcesInTheResultSet() {
+ return typeOfResourcesInTheResultSet;
+ }
+
+
+ /**
+ * @return List of resources that have matched the search query
+ * and/or specified filtering criteria.
+ */
+ public List<ResourceLink> getFoundItems() {
+ return (this.foundItems);
+ }
+
+
+ /**
+ * @return Number of resources that have matched the search query
+ * (and/or specified filtering criteria) that have already been
+ * fetched.
+ */
+ public int getFetchedItemCount() {
+ return (this.fullyFetchedItemCount);
+ }
+
+
+ /**
+ * @return Total number of resources that have matched the search query
+ * (and/or specified filtering criteria) - most of these will
+ * likely not be fetched yet.
+ */
+ public int getTotalMatchingItemCount() {
+ return (this.totalResultCount);
+ }
+
+
+ /**
+ * @return Total number of pages in the current result set.
+ */
+ public int getTotalResultPageNumber() {
+ int numberOfResourcesPerPageForThisResourceType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+ return (int)(Math.ceil((double)getTotalMatchingItemCount() / numberOfResourcesPerPageForThisResourceType));
+ }
+
+
+ /**
+ * List of matching items will be partial and populated sequentially
+ * based on user actions. Therefore, this method helps to check
+ * which list entries are still not populated.
+ *
+ * @param startIndex Beginning of the range to check.
+ * @param endIndex End of the range to check.
+ * @return Zero-based index of the first entry in the list of
+ * matching resources that hasn't been fetched yet.
+ * Will return <code>-1</code> if the provided range
+ * parameters are incorrect or if all items in the
+ * specified range are already available.
+ */
+ public int getFirstMatchingItemIndexNotYetFetched(int startIndex, int endIndex)
+ {
+ // check the specified range is correct
+ if (startIndex < 0 || endIndex > getTotalMatchingItemCount() - 1) {
+ return (-1);
+ }
+
+ // go through the search results in the specified range
+ // in an attempt to find an item that hasn't been fetched
+ // just yet
+ for (int i = startIndex; i <= endIndex; i++) {
+ ResourceLink item = this.foundItems.get(i);
+ if (item != null && item instanceof LoadingResource && !((LoadingResource)item).isLoading()) {
+ return (i);
+ }
+ }
+
+ // apparently, all items in the provided range are fetched
+ return (-1);
+ }
+
+
+
+ /**
+ * @param matchingItemIndex Index of the matching item from search results.
+ * @return Index (starting from "1") of page in the search results, where
+ * the matching item with a specified index is located. If the
+ * <code>matchingItemIndex</code> is wrong, <code>-1</code> is returned.
+ */
+ public int getMatchingItemPageNumberFor(int matchingItemIndex)
+ {
+ // check the specified index is correct
+ if (matchingItemIndex < 0 || matchingItemIndex > getTotalMatchingItemCount() - 1) {
+ return (-1);
+ }
+
+ int resultsPerPageForThisType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+ return (matchingItemIndex / resultsPerPageForThisType + 1);
+ }
+
+
+ /**
+ * @param resultPageNumber Number of the page, for which the calculations are to be done.
+ * @return Index of the first result entry on the specified result page. If <code>resultPageNumber</code>
+ * is less than <code>1</code> or greater than the total number of pages in the result set,
+ * a value of <code>-1</code> will be returned.
+ */
+ public int getFirstItemIndexOn(int resultPageNumber)
+ {
+ // page number must be in a valid range - starting with 1..onwards
+ if (resultPageNumber < 1 || resultPageNumber > getTotalResultPageNumber()) {
+ return (-1);
+ }
+
+ int numberOfResourcesPerPageForThisResourceType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+ return ((resultPageNumber - 1) * numberOfResourcesPerPageForThisResourceType);
+ }
+
+
+
+ /**
+ * Mainly for testing - outputs number of search results per item type.
+ */
+ public String toString()
+ {
+ // FIXME
+
+// StringBuilder out = new StringBuilder("Breakdown of item counts by type:\n");
+// for (Map.Entry<Integer,String> itemTypeNamePair : Resource.ALL_SUPPORTED_RESOURCE_COLLECTION_NAMES.entrySet()) {
+// out.append(itemTypeNamePair.getValue() + ": " +getFetchedItemCount(itemTypeNamePair.getKey()) +
+// "/" + getTotalItemCount(itemTypeNamePair.getKey()) + "\n");
+// }
+//
+// return (out.toString());
+
+ return ("search results... not implemented!!!");
+ }
+
+}