blob: 0e033cc5b61ffa26f91b447463e3ad573dc94c8e [file] [log] [blame]
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());
}
}
}