From dab00d5637fcd3229c07059011c0d365a6fc61a3 Mon Sep 17 00:00:00 2001 From: ramari16 Date: Mon, 4 Nov 2024 15:31:57 -0500 Subject: [PATCH] ALS-7778: Fix bottleneck in wildfly (#210) Co-authored-by: GeorgeC --- .../dbmi/avillach/security/JWTFilter.java | 2 +- .../dbmi/avillach/AggregateResourceIT.java | 2 +- .../harvard/dbmi/avillach/IRCTResourceIT.java | 2 +- .../dbmi/avillach/PicsureQueryServiceIT.java | 2 +- .../dbmi/avillach/SystemServiceIT.java | 2 +- .../hms/dbmi/avillach/PicSureInitializer.java | 4 +- .../AggregateDataSharingResourceRS.java | 246 +++---- .../hms/dbmi/avillach/GA4GHResourceRS.java | 3 +- .../hms/dbmi/avillach/HttpClientUtil.java | 5 +- .../resource/passthru/HttpClient.java | 63 -- .../passthru/PassThroughResourceRS.java | 613 +++++++++--------- .../passthru/PassThroughResourceRSTest.java | 13 +- .../dbmi/avillach/service/ProxyWebClient.java | 9 +- .../avillach/service/ResourceWebClient.java | 305 +++++---- .../service/ResourceWebClientTest.java | 391 +++++------ .../dbmi/avillach/util/HttpClientUtil.java | 138 ++-- 16 files changed, 897 insertions(+), 903 deletions(-) delete mode 100644 pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/HttpClient.java diff --git a/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/security/JWTFilter.java b/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/security/JWTFilter.java index ba6b19a0..1525dc7c 100755 --- a/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/security/JWTFilter.java +++ b/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/security/JWTFilter.java @@ -296,7 +296,7 @@ private HashMap prepareRequestMap(ContainerRequestContext reques Response formatResponse = resourceWebClient.queryFormat(resource.getResourceRSPath(), queryRequest); if (formatResponse.getStatus() == 200) { // add the formatted query if available - String formattedQuery = IOUtils.toString((InputStream) formatResponse.getEntity(), "UTF-8"); + String formattedQuery = (String) formatResponse.getEntity(); logger.debug("Formatted response: " + formattedQuery); requestMap.put("formattedQuery", formattedQuery); } diff --git a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/AggregateResourceIT.java b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/AggregateResourceIT.java index 1138fc44..7730c14d 100644 --- a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/AggregateResourceIT.java +++ b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/AggregateResourceIT.java @@ -145,7 +145,7 @@ public void testQuery() throws IOException { //Should throw an error if credentials missing or wrong System.out.println("401 URL: " + endpointUrl+"/query" + "|headers: " + headers + "|body: " + body); HttpResponse response = retrievePostResponse(endpointUrl+"/query", headers, body); -// System.out.println("Test Response: " + IOUtils.toString(response.getEntity().getContent(), "UTF-8")); +// System.out.println("Test Response: " + EntityUtils.toString(response.getEntity().getContent(), "UTF-8")); assertEquals("Missing credentials should return a 401", 401, response.getStatusLine().getStatusCode()); JsonNode responseMessage = objectMapper.readTree(response.getEntity().getContent()); assertNotNull("Response message should not be null", responseMessage); diff --git a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/IRCTResourceIT.java b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/IRCTResourceIT.java index 97f85611..3cd9dd09 100644 --- a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/IRCTResourceIT.java +++ b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/IRCTResourceIT.java @@ -408,7 +408,7 @@ public void testQueryResult() throws UnsupportedOperationException, IOException body = objectMapper.writeValueAsString(queryRequest); response = retrievePostResponse(irctEndpointUrl+"pic-sure/v1.4/query/"+testQueryResultId+"/result", headers, body); assertEquals("Correct request should return a 200",200, response.getStatusLine().getStatusCode()); - String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); + String responseBody = EntityUtils.toString(response.getEntity().getContent(), "UTF-8"); assertFalse("Response content should not be empty", responseBody.isEmpty()); } diff --git a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/PicsureQueryServiceIT.java b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/PicsureQueryServiceIT.java index cae7641e..82977041 100644 --- a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/PicsureQueryServiceIT.java +++ b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/PicsureQueryServiceIT.java @@ -150,7 +150,7 @@ public void testResult() throws Exception { //This should return some kind of result response = retrievePostResponse(uri, headers, objectMapper.writeValueAsString(resultRequest)); assertEquals("Correct request should return a 200", 200, response.getStatusLine().getStatusCode()); - String result = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); + String result = EntityUtils.toString(response.getEntity().getContent(), "UTF-8"); assertNotNull("Result should not be null, result"); //Nonexistent resultId diff --git a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/SystemServiceIT.java b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/SystemServiceIT.java index eb021d7e..3a578622 100755 --- a/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/SystemServiceIT.java +++ b/pic-sure-api-wildfly/src/test/java/edu/harvard/dbmi/avillach/SystemServiceIT.java @@ -22,7 +22,7 @@ public void testStatusIsAccessibleToSystemUser() throws Exception { get.setHeader(HttpHeaders.AUTHORIZATION, "Bearer "+ jwt); HttpResponse response = client.execute(get); assertEquals("Response status code should be 200", 200, response.getStatusLine().getStatusCode()); - assertEquals("System status should be RUNNING'", "RUNNING", IOUtils.toString(response.getEntity().getContent(), "UTF-8")); + assertEquals("System status should be RUNNING'", "RUNNING", EntityUtils.toString(response.getEntity().getContent(), "UTF-8")); }catch(Exception e) { fail("Exception: " + e.getMessage()); diff --git a/pic-sure-initializer/src/main/java/edu/harvard/hms/dbmi/avillach/PicSureInitializer.java b/pic-sure-initializer/src/main/java/edu/harvard/hms/dbmi/avillach/PicSureInitializer.java index a9e1dfa7..61be7f17 100644 --- a/pic-sure-initializer/src/main/java/edu/harvard/hms/dbmi/avillach/PicSureInitializer.java +++ b/pic-sure-initializer/src/main/java/edu/harvard/hms/dbmi/avillach/PicSureInitializer.java @@ -75,8 +75,8 @@ private static void parseCommandLine(String[] args) throws ParseException, FileN printHelpAndExit(); } url = cmd.getOptionValue("p"); - users = IOUtils.toString(new FileInputStream(cmd.getOptionValue("u")), "UTF-8"); - resources = IOUtils.toString(new FileInputStream(cmd.getOptionValue("r")), "UTF-8"); + users = EntityUtils.toString(new FileInputStream(cmd.getOptionValue("u")), "UTF-8"); + resources = EntityUtils.toString(new FileInputStream(cmd.getOptionValue("r")), "UTF-8"); token = cmd.getOptionValue("t"); } diff --git a/pic-sure-resources/pic-sure-aggregate-data-sharing-resource/src/main/java/edu/harvard/hms/dbmi/avillach/AggregateDataSharingResourceRS.java b/pic-sure-resources/pic-sure-aggregate-data-sharing-resource/src/main/java/edu/harvard/hms/dbmi/avillach/AggregateDataSharingResourceRS.java index 4b1b88e8..e3e7d7ad 100644 --- a/pic-sure-resources/pic-sure-aggregate-data-sharing-resource/src/main/java/edu/harvard/hms/dbmi/avillach/AggregateDataSharingResourceRS.java +++ b/pic-sure-resources/pic-sure-aggregate-data-sharing-resource/src/main/java/edu/harvard/hms/dbmi/avillach/AggregateDataSharingResourceRS.java @@ -18,6 +18,7 @@ import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; @@ -35,7 +36,7 @@ import java.util.stream.Stream; import static edu.harvard.dbmi.avillach.service.ResourceWebClient.QUERY_METADATA_FIELD; -import static edu.harvard.dbmi.avillach.util.HttpClientUtil.readObjectFromResponse; +import static edu.harvard.dbmi.avillach.util.HttpClientUtil.*; @Path("/aggregate-data-sharing") @Produces("application/json") @@ -66,6 +67,8 @@ public class AggregateDataSharingResourceRS implements IResourceRS { private final String randomSalt; + private final HttpClientUtil httpClientUtil; + public AggregateDataSharingResourceRS() { this(null); } @@ -88,7 +91,12 @@ public AggregateDataSharingResourceRS(ApplicationProperties applicationPropertie variance = properties.getTargetPicsureObfuscationVariance(); randomSalt = properties.getTargetPicsureObfuscationSalt(); - headers = new Header[]{new BasicHeader(HttpHeaders.AUTHORIZATION, BEARER_STRING + properties.getTargetPicsureToken())}; + headers = new Header[] {new BasicHeader(HttpHeaders.AUTHORIZATION, BEARER_STRING + properties.getTargetPicsureToken())}; + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); // Maximum total connections + connectionManager.setDefaultMaxPerRoute(20); // Maximum connections per route + httpClientUtil = HttpClientUtil.getInstance(connectionManager); } @@ -106,12 +114,13 @@ public ResourceInfo info(QueryRequest infoRequest) { logger.debug("Calling Aggregate Data Sharing Resource info()"); String pathName = "/info"; + HttpResponse response = null; try { QueryRequest chainRequest = new GeneralQueryRequest(); if (infoRequest != null) { chainRequest.setQuery(infoRequest.getQuery()); chainRequest.setResourceCredentials(infoRequest.getResourceCredentials()); - //set a default value of the existing uuid here (can override in properties file) + // set a default value of the existing uuid here (can override in properties file) chainRequest.setResourceUUID(infoRequest.getResourceUUID()); } if (properties.getTargetResourceId() != null && !properties.getTargetResourceId().isEmpty()) { @@ -120,25 +129,28 @@ public ResourceInfo info(QueryRequest infoRequest) { String payload = objectMapper.writeValueAsString(chainRequest); String composedURL = HttpClientUtil.composeURL(properties.getTargetPicsureUrl(), pathName); - HttpResponse response = HttpClientUtil.retrievePostResponse(composedURL, headers, payload); + response = httpClientUtil.retrievePostResponse(composedURL, headers, payload); if (!HttpClientUtil.is2xx(response)) { - logger.error("{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + logger.error( + "{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); HttpClientUtil.throwResponseError(response, properties.getTargetPicsureUrl()); } - //if we are proxying an info request, we need to return our own resource ID + // if we are proxying an info request, we need to return our own resource ID ResourceInfo resourceInfo = readObjectFromResponse(response, ResourceInfo.class); if (infoRequest != null && infoRequest.getResourceUUID() != null) { resourceInfo.setId(infoRequest.getResourceUUID()); } return resourceInfo; } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + infoRequest.getResourceUUID()); + throw new ApplicationException("Error encoding query for resource with id " + infoRequest.getResourceUUID()); } catch (ClassCastException | IllegalArgumentException e) { logger.error(e.getMessage()); throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + HttpClientUtil.closeHttpResponse(response); } } @@ -148,8 +160,13 @@ public ResourceInfo info(QueryRequest infoRequest) { public SearchResults search(QueryRequest searchRequest) { logger.debug("Calling Aggregate Data Sharing Search"); checkQuery(searchRequest); - HttpResponse response = postRequest(searchRequest, "/search"); - return readObjectFromResponse(response, SearchResults.class); + HttpResponse response = null; + try { + response = postRequest(searchRequest, "/search"); + return readObjectFromResponse(response, SearchResults.class); + } finally { + HttpClientUtil.closeHttpResponse(response); + } } @POST @@ -158,9 +175,13 @@ public SearchResults search(QueryRequest searchRequest) { public QueryStatus query(QueryRequest queryRequest) { logger.debug("Calling Aggregate Data Sharing Resource query()"); checkQuery(queryRequest); - HttpResponse response = postRequest(queryRequest, "/query"); - return readObjectFromResponse(response, QueryStatus.class); - + HttpResponse response = null; + try { + response = postRequest(queryRequest, "/query"); + return readObjectFromResponse(response, QueryStatus.class); + } finally { + HttpClientUtil.closeHttpResponse(response); + } } @POST @@ -169,8 +190,13 @@ public QueryStatus query(QueryRequest queryRequest) { public QueryStatus queryStatus(@PathParam("resourceQueryId") UUID queryId, QueryRequest statusRequest) { logger.debug("Calling Aggregate Data Sharing Resource queryStatus() for query {}", queryId); checkQuery(statusRequest); - HttpResponse response = postRequest(statusRequest, "/query/" + queryId + "/status"); - return readObjectFromResponse(response, QueryStatus.class); + HttpResponse response = null; + try { + response = postRequest(statusRequest, "/query/" + queryId + "/status"); + return readObjectFromResponse(response, QueryStatus.class); + } finally { + HttpClientUtil.closeHttpResponse(response); + } } @POST @@ -181,11 +207,10 @@ public Response queryResult(@PathParam("resourceQueryId") UUID queryId, QueryReq checkQuery(resultRequest); HttpResponse response = postRequest(resultRequest, "/query/" + queryId + "/result"); try { - return Response.ok(response.getEntity().getContent()).build(); - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + resultRequest.getResourceUUID() - ); + String responseBody = httpClientUtil.readObjectFromResponse(response); + return Response.ok(responseBody).build(); + } finally { + HttpClientUtil.closeHttpResponse(response); } } @@ -194,18 +219,19 @@ private HttpResponse postRequest(QueryRequest statusRequest, String pathName) { QueryRequest chainRequest = createChainRequest(statusRequest); String payload = objectMapper.writeValueAsString(chainRequest); String composedURL = HttpClientUtil.composeURL(properties.getTargetPicsureUrl(), pathName); - HttpResponse response = HttpClientUtil.retrievePostResponse(composedURL, headers, payload); + HttpResponse response = httpClientUtil.retrievePostResponse(composedURL, headers, payload); if (!HttpClientUtil.is2xx(response)) { - logger.error("{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + logger.error( + "{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); HttpClientUtil.throwResponseError(response, properties.getTargetPicsureUrl()); } return response; } catch (IOException e) { // Note: this shouldn't ever happen logger.error("Error encoding search payload", e); - throw new ApplicationException( - "Error encoding search for resource with id " + statusRequest.getResourceUUID()); + throw new ApplicationException("Error encoding search for resource with id " + statusRequest.getResourceUUID()); } } @@ -216,6 +242,7 @@ public Response querySync(QueryRequest queryRequest) { logger.debug("Calling Aggregate Data Sharing Resource querySync()"); checkQuery(queryRequest); + HttpResponse response = null; try { Object query = queryRequest.getQuery(); UUID resourceUUID = queryRequest.getResourceUUID(); @@ -227,9 +254,8 @@ public Response querySync(QueryRequest queryRequest) { String expectedResultType = jsonNode.get("expectedResultType").asText(); Set allowedResultTypes = Set.of( - "COUNT", "CROSS_COUNT", "INFO_COLUMN_LISTING", "OBSERVATION_COUNT", - "OBSERVATION_CROSS_COUNT", "CATEGORICAL_CROSS_COUNT", "CONTINUOUS_CROSS_COUNT", - "VARIANT_COUNT_FOR_QUERY", "AGGREGATE_VCF_EXCERPT", "VCF_EXCERPT" + "COUNT", "CROSS_COUNT", "INFO_COLUMN_LISTING", "OBSERVATION_COUNT", "OBSERVATION_CROSS_COUNT", "CATEGORICAL_CROSS_COUNT", + "CONTINUOUS_CROSS_COUNT", "VARIANT_COUNT_FOR_QUERY", "AGGREGATE_VCF_EXCERPT", "VCF_EXCERPT" ); if (!allowedResultTypes.contains(expectedResultType)) { @@ -237,7 +263,7 @@ public Response querySync(QueryRequest queryRequest) { return Response.status(Response.Status.BAD_REQUEST).build(); } - HttpResponse response = getHttpResponse(queryRequest, resourceUUID, "/query/sync", properties.getTargetPicsureUrl()); + response = getHttpResponse(queryRequest, resourceUUID, "/query/sync", properties.getTargetPicsureUrl()); HttpEntity entity = response.getEntity(); String entityString = EntityUtils.toString(entity, "UTF-8"); @@ -245,7 +271,7 @@ public Response querySync(QueryRequest queryRequest) { responseString = getExpectedResponse(expectedResultType, entityString, responseString, queryRequest); - //propagate any metadata from the back end (e.g., resultId) + // propagate any metadata from the back end (e.g., resultId) if (response.containsHeader(QUERY_METADATA_FIELD)) { Header metadataHeader = ((Header[]) response.getHeaders(QUERY_METADATA_FIELD))[0]; return Response.ok(responseString).header(QUERY_METADATA_FIELD, metadataHeader.getValue()).build(); @@ -254,44 +280,45 @@ public Response querySync(QueryRequest queryRequest) { return Response.ok(responseString).build(); } catch (IOException e) { logger.error(e.getMessage(), e); - throw new ApplicationException( - "Error encoding query for resource with id " + queryRequest.getResourceUUID()); + throw new ApplicationException("Error encoding query for resource with id " + queryRequest.getResourceUUID()); } catch (ClassCastException | IllegalArgumentException e) { logger.error(e.getMessage()); throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); } } - private HttpResponse getHttpResponse(QueryRequest queryRequest, UUID resourceUUID, String pathName, String targetPicsureUrl) throws JsonProcessingException { + private HttpResponse getHttpResponse(QueryRequest queryRequest, UUID resourceUUID, String pathName, String targetPicsureUrl) + throws JsonProcessingException { String queryString = objectMapper.writeValueAsString(queryRequest); String composedURL = HttpClientUtil.composeURL(targetPicsureUrl, pathName); logger.debug("Aggregate Data Sharing Resource, sending query: " + queryString + ", to: " + composedURL); - HttpResponse response = HttpClientUtil.retrievePostResponse(composedURL, headers, queryString); + HttpResponse response = httpClientUtil.retrievePostResponse(composedURL, headers, queryString); if (!HttpClientUtil.is2xx(response)) { logger.error("Not 200 status!"); logger.error( - composedURL + " calling resource with id " + resourceUUID + " did not return a 200: {} {} ", - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + composedURL + " calling resource with id " + resourceUUID + " did not return a 200: {} {} ", + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); HttpClientUtil.throwResponseError(response, targetPicsureUrl); } return response; } /** - * This method will process the response from the backend and return the - * expected response based on the expected result type. - * Currently, the only types that are handled are: - * COUNT, CROSS_COUNT, CATEGORICAL_CROSS_COUNT, CONTINUOUS_CROSS_COUNT + * This method will process the response from the backend and return the expected response based on the expected result type. Currently, + * the only types that are handled are: COUNT, CROSS_COUNT, CATEGORICAL_CROSS_COUNT, CONTINUOUS_CROSS_COUNT * * @param expectedResultType The expected result type - * @param entityString The response from the backend that will be processed - * @param responseString The response that will be returned. Will return the passed entityString if - * no cases are matched. + * @param entityString The response from the backend that will be processed + * @param responseString The response that will be returned. Will return the passed entityString if no cases are matched. * @return String The response that will be returned * @throws JsonProcessingException If there is an error processing the response */ - private String getExpectedResponse(String expectedResultType, String entityString, String responseString, QueryRequest queryRequest) throws IOException, JsonProcessingException { + private String getExpectedResponse(String expectedResultType, String entityString, String responseString, QueryRequest queryRequest) + throws IOException, JsonProcessingException { String crossCountResponse; switch (expectedResultType) { case "COUNT": @@ -318,8 +345,8 @@ private String getExpectedResponse(String expectedResultType, String entityStrin } /** - * No matter what the expected result type is we will get the cross count instead. Additionally, - * it will include ALL study consents in the query. + * No matter what the expected result type is we will get the cross count instead. Additionally, it will include ALL study consents in + * the query. * * @param queryRequest The query request * @return String The cross count for the query @@ -327,7 +354,9 @@ private String getExpectedResponse(String expectedResultType, String entityStrin private String getCrossCountForQuery(QueryRequest queryRequest) throws IOException { logger.debug("Calling Aggregate Data Sharing Resource getCrossCountForQuery()"); - HttpResponse response = getHttpResponse(changeQueryToOpenCrossCount(queryRequest), queryRequest.getResourceUUID(), "/query/sync", properties.getTargetPicsureUrl()); + HttpResponse response = getHttpResponse( + changeQueryToOpenCrossCount(queryRequest), queryRequest.getResourceUUID(), "/query/sync", properties.getTargetPicsureUrl() + ); HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, "UTF-8"); } @@ -347,8 +376,7 @@ private QueryRequest changeQueryToOpenCrossCount(QueryRequest queryRequest) { JsonNode updatedExpectedResulType = setExpectedResultTypeToCrossCount(jsonNode); JsonNode includesStudyConsents = addStudyConsentsToQuery(updatedExpectedResulType); - LinkedHashMap rebuiltQuery = objectMapper.convertValue(includesStudyConsents, new TypeReference<>() { - }); + LinkedHashMap rebuiltQuery = objectMapper.convertValue(includesStudyConsents, new TypeReference<>() {}); queryRequest.setQuery(rebuiltQuery); return queryRequest; } @@ -371,11 +399,9 @@ private JsonNode addStudyConsentsToQuery(JsonNode jsonNode) { logger.debug("Calling Aggregate Data Sharing Resource addStudyConsentsToQuery()"); SearchResults consentResults = getAllStudyConsents(); - LinkedHashMap linkedHashMap = objectMapper.convertValue(consentResults.getResults(), new TypeReference<>() { - }); + LinkedHashMap linkedHashMap = objectMapper.convertValue(consentResults.getResults(), new TypeReference<>() {}); Object phenotypes = linkedHashMap.get("phenotypes"); - LinkedHashMap phenotypesLinkedHashMap = objectMapper.convertValue(phenotypes, new TypeReference<>() { - }); + LinkedHashMap phenotypesLinkedHashMap = objectMapper.convertValue(phenotypes, new TypeReference<>() {}); // get all the keys from phenotypes Set keys = phenotypesLinkedHashMap.keySet(); @@ -411,30 +437,32 @@ public Response queryFormat(QueryRequest queryRequest) { UUID resourceUUID = queryRequest.getResourceUUID(); String pathName = "/query/format"; + HttpResponse response = null; try { String queryString = objectMapper.writeValueAsString(queryRequest); String composedURL = HttpClientUtil.composeURL(properties.getTargetPicsureUrl(), pathName); - HttpResponse response = HttpClientUtil.retrievePostResponse(composedURL, headers, queryString); + response = httpClientUtil.retrievePostResponse(composedURL, headers, queryString); if (!HttpClientUtil.is2xx(response)) { logger.error( - composedURL + " calling resource with id " + resourceUUID + " did not return a 200: {} {} ", - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + composedURL + " calling resource with id " + resourceUUID + " did not return a 200: {} {} ", + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); HttpClientUtil.throwResponseError(response, properties.getTargetPicsureUrl()); } return Response.ok(response.getEntity().getContent()).build(); } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + queryRequest.getResourceUUID()); + throw new ApplicationException("Error encoding query for resource with id " + queryRequest.getResourceUUID()); } catch (ClassCastException | IllegalArgumentException e) { logger.error(e.getMessage()); throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + HttpClientUtil.closeHttpResponse(response); } } private Map processCrossCounts(String entityString) throws com.fasterxml.jackson.core.JsonProcessingException { - Map crossCounts = objectMapper.readValue(entityString, new TypeReference<>() { - }); + Map crossCounts = objectMapper.readValue(entityString, new TypeReference<>() {}); int requestVariance = generateVarianceWithCrossCounts(crossCounts); crossCounts = obfuscateCrossCounts(crossCounts, requestVariance); @@ -445,7 +473,7 @@ private Map processCrossCounts(String entityString) throws com.f /** * This method will appropriately process the obfuscation of the cross counts. * - * @param crossCounts The cross counts + * @param crossCounts The cross counts * @param requestVariance The variance for the request * @return Map The obfuscated cross counts */ @@ -473,11 +501,9 @@ private Map obfuscateCrossCounts(Map crossCounts } /** - * This method is used to generate a variance for Cross Count queries. - * The variance is generated by taking the cross counts and sorting them by key. - * Then we generate a string with lines like consent:1\n consent:2\ consent:3\n etc. - * Then we generate a variance using the string. This is to give us a random variance that is deterministic for each - * query. + * This method is used to generate a variance for Cross Count queries. The variance is generated by taking the cross counts and sorting + * them by key. Then we generate a string with lines like consent:1\n consent:2\ consent:3\n etc. Then we generate a variance using the + * string. This is to give us a random variance that is deterministic for each query. * * @param crossCounts A map of cross counts * @return int The variance @@ -498,27 +524,27 @@ private int generateVarianceWithCrossCounts(Map crossCounts) { } /** - * This method will return an obfuscated binned count of continuous crosses. Due to the format of a continuous - * cross count, we are unable to directly obfuscate it in its original form. First, we send the continuous - * cross count data to the visualization resource to group it into bins. Once the data is binned, we assess whether - * obfuscation is necessary for this particular continuous cross count. If obfuscation is not required, we return - * the data in string format. However, if obfuscation is needed, we first obfuscate the data and then return it. + * This method will return an obfuscated binned count of continuous crosses. Due to the format of a continuous cross count, we are + * unable to directly obfuscate it in its original form. First, we send the continuous cross count data to the visualization resource to + * group it into bins. Once the data is binned, we assess whether obfuscation is necessary for this particular continuous cross count. + * If obfuscation is not required, we return the data in string format. However, if obfuscation is needed, we first obfuscate the data + * and then return it. * * @param continuousCrossCountResponse The continuous cross count response - * @param crossCountResponse The cross count response - * @param queryRequest The original query request + * @param crossCountResponse The cross count response + * @param queryRequest The original query request * @return String The obfuscated binned continuous cross count * @throws IOException If there is an error processing the JSON */ - protected String processContinuousCrossCounts(String continuousCrossCountResponse, String crossCountResponse, QueryRequest queryRequest) throws IOException { + protected String processContinuousCrossCounts(String continuousCrossCountResponse, String crossCountResponse, QueryRequest queryRequest) + throws IOException { logger.info("Processing continuous cross counts"); if (continuousCrossCountResponse == null || crossCountResponse == null) { return null; } - Map crossCounts = objectMapper.readValue(crossCountResponse, new TypeReference<>() { - }); + Map crossCounts = objectMapper.readValue(crossCountResponse, new TypeReference<>() {}); int generatedVariance = this.generateVarianceWithCrossCounts(crossCounts); boolean mustObfuscate = isCrossCountObfuscated(crossCounts, generatedVariance); @@ -528,8 +554,10 @@ protected String processContinuousCrossCounts(String continuousCrossCountRespons // Handle the case where there is no visualization service UUID if (properties.getVisualizationResourceId() != null) { - Map> continuousCrossCounts = objectMapper.readValue(continuousCrossCountResponse, new TypeReference<>() {}); - Map> binnedContinuousCrossCounts = getBinnedContinuousCrossCount(queryRequest, continuousCrossCounts); + Map> continuousCrossCounts = + objectMapper.readValue(continuousCrossCountResponse, new TypeReference<>() {}); + Map> binnedContinuousCrossCounts = + getBinnedContinuousCrossCount(queryRequest, continuousCrossCounts); // Log the binned continuous cross counts logger.info("Binned continuous cross counts: {}", binnedContinuousCrossCounts); @@ -545,14 +573,17 @@ protected String processContinuousCrossCounts(String continuousCrossCountRespons return continuousCrossCountResponse; } - Map> continuousCrossCounts = objectMapper.readValue(continuousCrossCountResponse, new TypeReference<>() {}); + Map> continuousCrossCounts = + objectMapper.readValue(continuousCrossCountResponse, new TypeReference<>() {}); obfuscatedCrossCount(generatedVariance, continuousCrossCounts); return objectMapper.writeValueAsString(continuousCrossCounts); } } - private Map> getBinnedContinuousCrossCount(QueryRequest queryRequest, Map> continuousCrossCounts) throws IOException { + private Map> getBinnedContinuousCrossCount( + QueryRequest queryRequest, Map> continuousCrossCounts + ) throws IOException { // Create Query for Visualization /bin/continuous QueryRequest visualizationBinRequest = new GeneralQueryRequest(); visualizationBinRequest.setResourceUUID(properties.getVisualizationResourceId()); @@ -565,38 +596,38 @@ private Map> getBinnedContinuousCrossCount(QueryRequ } // call the binning endpoint - HttpResponse httpResponse = getHttpResponse(visualizationBinRequest, visualizationBinRequest.getResourceUUID(), "/bin/continuous", visResource.getResourceRSPath()); + HttpResponse httpResponse = getHttpResponse( + visualizationBinRequest, visualizationBinRequest.getResourceUUID(), "/bin/continuous", visResource.getResourceRSPath() + ); HttpEntity entity = httpResponse.getEntity(); String binResponse = EntityUtils.toString(entity, "UTF-8"); - return objectMapper.readValue(binResponse, new TypeReference>>() { - }); + return objectMapper.readValue(binResponse, new TypeReference>>() {}); } /** - * This method handles the processing of categorical cross counts. It begins by determining whether the cross - * counts require obfuscation. This is accomplished by checking if any of the CROSS_COUNTS must be obfuscated. - * If obfuscation is required, the categorical cross counts will be obfuscated accordingly. Otherwise, - * if no obfuscation is needed, the method can simply return the categorical entity string. + * This method handles the processing of categorical cross counts. It begins by determining whether the cross counts require + * obfuscation. This is accomplished by checking if any of the CROSS_COUNTS must be obfuscated. If obfuscation is required, the + * categorical cross counts will be obfuscated accordingly. Otherwise, if no obfuscation is needed, the method can simply return the + * categorical entity string. * * @param categoricalEntityString The categorical entity string - * @param crossCountEntityString The cross count entity string + * @param crossCountEntityString The cross count entity string * @return String The processed categorical entity string * @throws JsonProcessingException If there is an error processing the JSON */ - protected String processCategoricalCrossCounts(String categoricalEntityString, String crossCountEntityString) throws JsonProcessingException { + protected String processCategoricalCrossCounts(String categoricalEntityString, String crossCountEntityString) + throws JsonProcessingException { logger.info("Processing categorical cross counts"); if (categoricalEntityString == null || crossCountEntityString == null) { return null; } - Map crossCounts = objectMapper.readValue(crossCountEntityString, new TypeReference<>() { - }); + Map crossCounts = objectMapper.readValue(crossCountEntityString, new TypeReference<>() {}); int generatedVariance = this.generateVarianceWithCrossCounts(crossCounts); - Map> categoricalCrossCount = objectMapper.readValue(categoricalEntityString, new TypeReference<>() { - }); + Map> categoricalCrossCount = objectMapper.readValue(categoricalEntityString, new TypeReference<>() {}); if (categoricalCrossCount == null) { logger.info("Categorical cross count is null. Returning categoricalEntityString: {}", categoricalEntityString); @@ -632,8 +663,7 @@ private boolean doObfuscateData(Map> categoricalCros return false; } - return categoricalCrossCount.values().stream() - .anyMatch(this::checkForObfuscation); + return categoricalCrossCount.values().stream().anyMatch(this::checkForObfuscation); } private boolean checkForObfuscation(Map axisMap) { @@ -669,11 +699,11 @@ private String getRequestSource() { } /** - * This method will obfuscate the cross counts based on the generated variance. We do not have a return because - * we are modifying the passed crossCount object. Java passes objects by reference value, so we do not need to return. + * This method will obfuscate the cross counts based on the generated variance. We do not have a return because we are modifying the + * passed crossCount object. Java passes objects by reference value, so we do not need to return. * * @param generatedVariance The variance for the request - * @param crossCount The cross count that will be obfuscated + * @param crossCount The cross count that will be obfuscated */ private void obfuscatedCrossCount(int generatedVariance, Map> crossCount) { crossCount.forEach((key, value) -> { @@ -689,10 +719,10 @@ private void obfuscatedCrossCount(int generatedVariance, Map crossCounts) { } /** - * This method will generate a random variance for the request based on the passed entityString. The variance - * will be between -variance and +variance. The variance will be generated by adding a random salt to the - * entityString and then taking the hashcode of the result. The variance will be the hashcode mod the - * variance * 2 + 1 - variance. + * This method will generate a random variance for the request based on the passed entityString. The variance will be between -variance + * and +variance. The variance will be generated by adding a random salt to the entityString and then taking the hashcode of the result. + * The variance will be the hashcode mod the variance * 2 + 1 - variance. * * @return int The variance for the request */ @@ -764,9 +793,8 @@ private Stream generateParents(String key) { String[] split = key.split("\\\\"); if (split.length > 1) { - return Arrays.stream(Arrays.copyOfRange(split, 0, split.length - 1)) - .filter(Predicate.not(String::isEmpty)) - .map(segment -> stringJoiner.add(segment).toString()); + return Arrays.stream(Arrays.copyOfRange(split, 0, split.length - 1)).filter(Predicate.not(String::isEmpty)) + .map(segment -> stringJoiner.add(segment).toString()); } return Stream.empty(); } diff --git a/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/GA4GHResourceRS.java b/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/GA4GHResourceRS.java index d83935c5..b7d6aef0 100644 --- a/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/GA4GHResourceRS.java +++ b/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/GA4GHResourceRS.java @@ -22,6 +22,7 @@ import edu.harvard.dbmi.avillach.service.IResourceRS; import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -278,7 +279,7 @@ public QueryStatus query(QueryRequest queryJson) { // If the HTTP Response is a success, then returns an object like so: {"resultId":230464} //TODO later Add things like duration and expiration try { - String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); + String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); JsonNode responseNode = json.readTree(responseBody); long endtime = new Date().getTime(); diff --git a/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/HttpClientUtil.java b/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/HttpClientUtil.java index e6320fb4..2353ad26 100644 --- a/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/HttpClientUtil.java +++ b/pic-sure-resources/pic-sure-ga4gh-dos/src/main/java/edu/harvard/hms/dbmi/avillach/HttpClientUtil.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import edu.harvard.dbmi.avillach.util.exception.ResourceCommunicationException; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +57,7 @@ public static HttpResponse retrievePostResponse(String uri, String token) { public static List readListFromResponse(HttpResponse response, Class expectedElementType) { try { - String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); + String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); return json.readValue(responseBody, new TypeReference>() {}); } catch (IOException e) { e.printStackTrace(); @@ -66,7 +67,7 @@ public static List readListFromResponse(HttpResponse response, Class e public static List readDataObjectsFromResponse(HttpResponse response, Class expectedElementType) { try { - String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); + String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); // Get only the data_objects field from the returned structure. Ugly, but has to de- and then re-serialize JsonNode jn = json.readTree(responseBody); if (null == jn.get("data_objects")) { diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/HttpClient.java b/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/HttpClient.java deleted file mode 100644 index f929f91e..00000000 --- a/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/HttpClient.java +++ /dev/null @@ -1,63 +0,0 @@ -package edu.harvard.hms.dbmi.avillach.resource.passthru; - -import javax.enterprise.context.ApplicationScoped; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import edu.harvard.dbmi.avillach.util.exception.ResourceInterfaceException; - -/** - * Wrapper class for HttpClientUtil in order used to support unit tests and mocking of responses. - * - * @author nixl5s - * - */ -@ApplicationScoped -public class HttpClient { - private static final int RETRY_LIMIT = 5; - - private static final Logger logger = LoggerFactory.getLogger(HttpClient.class); - - public String composeURL(String baseURL, String pathName) { - return edu.harvard.dbmi.avillach.util.HttpClientUtil.composeURL(baseURL, pathName); - } - - public T readObjectFromResponse(HttpResponse response, Class expectedElementType) { - return edu.harvard.dbmi.avillach.util.HttpClientUtil.readObjectFromResponse(response, expectedElementType); - } - -// public HttpResponse retrieveGetResponse(String uri, Header[] headers) { -// return edu.harvard.dbmi.avillach.util.HttpClientUtil.retrieveGetResponse(uri, headers); -// } - - public HttpResponse retrievePostResponse(String uri, Header[] headers, String body) { - - HttpResponse response = null; - for(int i = 1; i <= RETRY_LIMIT && response == null; i++) { - try { - response = edu.harvard.dbmi.avillach.util.HttpClientUtil.retrievePostResponse(uri, headers, body); - break; - } catch (ResourceInterfaceException e) { - if(i < RETRY_LIMIT ) { - logger.warn("Failed to contact remote server. Retrying"); - } else { - logger.error("Failed to contact remote server. Giving up!"); - throw e; - } - } - } - - return response; - } - - public void throwResponseError(HttpResponse response, String baseURL) { - edu.harvard.dbmi.avillach.util.HttpClientUtil.throwResponseError(response, baseURL); - } - - public void throwInternalResponseError(HttpResponse response, String baseURL) { - edu.harvard.dbmi.avillach.util.HttpClientUtil.throwInternalResponseError(response, baseURL); - } -} diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRS.java b/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRS.java index 55727904..ecf06b61 100644 --- a/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRS.java +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/main/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRS.java @@ -5,14 +5,15 @@ import javax.inject.Inject; import javax.ws.rs.*; -import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; +import edu.harvard.dbmi.avillach.util.HttpClientUtil; import org.apache.http.Header; import org.apache.http.HttpResponse; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,300 +24,334 @@ import edu.harvard.dbmi.avillach.util.exception.ApplicationException; import edu.harvard.dbmi.avillach.util.exception.ProtocolException; import static edu.harvard.dbmi.avillach.service.ResourceWebClient.QUERY_METADATA_FIELD; +import static edu.harvard.dbmi.avillach.util.HttpClientUtil.closeHttpResponse; @Path("/passthru") @Produces("application/json") @Consumes("application/json") public class PassThroughResourceRS implements IResourceRS { - private static final String BEARER_STRING = "Bearer "; - - private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final Logger logger = LoggerFactory.getLogger(PassThroughResourceRS.class); - - @Inject - private ApplicationProperties properties; - @Inject - private HttpClient httpClient; - - public PassThroughResourceRS() { - } - - @Inject - public PassThroughResourceRS(ApplicationProperties applicationProperties, HttpClient httpClient) { - this.properties = applicationProperties; - this.httpClient = httpClient; - } - - @POST - @Path("/info") - public ResourceInfo info(QueryRequest infoRequest) { - String pathName = "/info"; - - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - if (infoRequest != null) { - chainRequest.setQuery(infoRequest.getQuery()); - chainRequest.setResourceCredentials(infoRequest.getResourceCredentials()); - } - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - - ResourceInfo resourceInfo = httpClient.readObjectFromResponse(response, ResourceInfo.class); - if (infoRequest != null && infoRequest.getResourceUUID() != null) { - resourceInfo.setId(infoRequest.getResourceUUID()); - } - return resourceInfo; - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + infoRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - @POST - @Path("/query") - public QueryStatus query(QueryRequest queryRequest) { - if (queryRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Object search = queryRequest.getQuery(); - if (search == null) { - throw new ProtocolException((ProtocolException.MISSING_DATA)); - } - - String pathName = "/query"; - - try { - QueryRequest chainRequest = queryRequest.copy(); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} calling resource with id {} did not return a 200: {} {} ", - properties.getTargetPicsureUrl(), pathName, chainRequest.getResourceUUID(), - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - QueryStatus queryStatus = httpClient.readObjectFromResponse(response, QueryStatus.class); - queryStatus.setResourceID(queryRequest.getResourceUUID()); - return queryStatus; - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + queryRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + private static final String BEARER_STRING = "Bearer "; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final Logger logger = LoggerFactory.getLogger(PassThroughResourceRS.class); + + @Inject + private ApplicationProperties properties; + + private final HttpClientUtil httpClient; + + public PoolingHttpClientConnectionManager getConnectionManager() { + PoolingHttpClientConnectionManager connectionManager; + connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); // Maximum total connections + connectionManager.setDefaultMaxPerRoute(20); // Maximum connections per route + return connectionManager; + } + + public PassThroughResourceRS() { + this.httpClient = HttpClientUtil.getInstance(getConnectionManager()); + } + + @Inject + public PassThroughResourceRS(ApplicationProperties applicationProperties) { + this.properties = applicationProperties; + this.httpClient = HttpClientUtil.getInstance(getConnectionManager()); + } + + public PassThroughResourceRS(ApplicationProperties properties, HttpClientUtil httpClient) { + this.properties = properties; + this.httpClient = httpClient; + } + + @POST + @Path("/info") + public ResourceInfo info(QueryRequest infoRequest) { + String pathName = "/info"; + + HttpResponse response = null; + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + if (infoRequest != null) { + chainRequest.setQuery(infoRequest.getQuery()); + chainRequest.setResourceCredentials(infoRequest.getResourceCredentials()); + } + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + + response = httpClient + .retrievePostResponse(HttpClientUtil.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + HttpClientUtil.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + + ResourceInfo resourceInfo = HttpClientUtil.readObjectFromResponse(response, ResourceInfo.class); + if (infoRequest != null && infoRequest.getResourceUUID() != null) { + resourceInfo.setId(infoRequest.getResourceUUID()); + } + return resourceInfo; + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + infoRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); } - } - - @POST - @Path("/query/{resourceQueryId}/result") - public Response queryResult(@PathParam("resourceQueryId") String queryId, QueryRequest resultRequest) { - if (resultRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - - String pathName = "/query/" + queryId + "/result"; - - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - chainRequest.setQuery(resultRequest.getQuery()); - chainRequest.setResourceCredentials(resultRequest.getResourceCredentials()); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + } + + @POST + @Path("/query") + public QueryStatus query(QueryRequest queryRequest) { + if (queryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Object search = queryRequest.getQuery(); + if (search == null) { + throw new ProtocolException((ProtocolException.MISSING_DATA)); + } + + String pathName = "/query"; + HttpResponse response = null; + try { + QueryRequest chainRequest = queryRequest.copy(); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} calling resource with id {} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + chainRequest.getResourceUUID(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + QueryStatus queryStatus = httpClient.readObjectFromResponse(response, QueryStatus.class); + queryStatus.setResourceID(queryRequest.getResourceUUID()); + return queryStatus; + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + queryRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); + } + } + + @POST + @Path("/query/{resourceQueryId}/result") + public Response queryResult(@PathParam("resourceQueryId") String queryId, QueryRequest resultRequest) { + if (resultRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + + String pathName = "/query/" + queryId + "/result"; + + HttpResponse response = null; + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + chainRequest.setQuery(resultRequest.getQuery()); + chainRequest.setResourceCredentials(resultRequest.getResourceCredentials()); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + String content = httpClient.readObjectFromResponse(response); if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} calling resource with id {} did not return a 200: {} {} ", - properties.getTargetPicsureUrl(), pathName, chainRequest.getResourceUUID(), - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - - return Response.ok(response.getEntity().getContent()).build(); - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + resultRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - @POST - @Path("/query/{resourceQueryId}/status") - public QueryStatus queryStatus(@PathParam("resourceQueryId") String queryId, QueryRequest statusRequest) { - // JNix: Retaining for future use... - if (statusRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); + logger.error( + "{}{} calling resource with id {} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + chainRequest.getResourceUUID(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + + return Response.ok(content).build(); + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + resultRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); } - - String pathName = "/query/" + queryId + "/status"; - - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - chainRequest.setQuery(statusRequest.getQuery()); - chainRequest.setResourceCredentials(statusRequest.getResourceCredentials()); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} calling resource with id {} did not return a 200: {} {} ", - properties.getTargetPicsureUrl(), pathName, chainRequest.getResourceUUID(), - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - QueryStatus queryStatus = httpClient.readObjectFromResponse(response, QueryStatus.class); - queryStatus.setResourceID(statusRequest.getResourceUUID()); - return queryStatus; - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + statusRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - @POST - @Path("/query/sync") - @Override - public Response querySync(QueryRequest queryRequest) { - if (queryRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Object search = queryRequest.getQuery(); - if (search == null) { - throw new ProtocolException((ProtocolException.MISSING_DATA)); - } - - String pathName = "/query/sync"; - - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - chainRequest.setQuery(queryRequest.getQuery()); - chainRequest.setResourceCredentials(queryRequest.getResourceCredentials()); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} calling resource with id {} did not return a 200: {} {} ", - properties.getTargetPicsureUrl(), pathName, chainRequest.getResourceUUID(), - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - - if (response.containsHeader(QUERY_METADATA_FIELD)) { - Header metadataHeader = ((Header[]) response.getHeaders(QUERY_METADATA_FIELD))[0]; - logger.debug("Found Header[] : " + metadataHeader.getValue()); - return Response.ok(response.getEntity().getContent()) - .header(QUERY_METADATA_FIELD, metadataHeader.getValue()).build(); - } - - return Response.ok(response.getEntity().getContent()).build(); - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + queryRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - @POST - @Path("/search") - public SearchResults search(QueryRequest searchRequest) { - if (searchRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Object search = searchRequest.getQuery(); - if (search == null) { - throw new ProtocolException((ProtocolException.MISSING_DATA)); - } - - String pathName = "/search/" + properties.getTargetResourceId(); - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - chainRequest.setQuery(searchRequest.getQuery()); - chainRequest.setResourceCredentials(searchRequest.getResourceCredentials()); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - return httpClient.readObjectFromResponse(response, SearchResults.class); - } catch (IOException e) { - // Note: this shouldn't ever happen - logger.error("Error encoding search payload", e); - throw new ApplicationException( - "Error encoding search for resource with id " + searchRequest.getResourceUUID()); - } - } - - @Override - public Response queryFormat(QueryRequest queryRequest) { - if (queryRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Object search = queryRequest.getQuery(); - if (search == null) { - throw new ProtocolException((ProtocolException.MISSING_DATA)); - } - - String pathName = "/query/format"; - - try { - QueryRequest chainRequest = new GeneralQueryRequest(); - chainRequest.setQuery(queryRequest.getQuery()); - chainRequest.setResourceCredentials(queryRequest.getResourceCredentials()); - chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); - - String payload = objectMapper.writeValueAsString(chainRequest); - HttpResponse response = httpClient.retrievePostResponse( - httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); - if (response.getStatusLine().getStatusCode() != 200) { - logger.error("{}{} calling resource with id {} did not return a 200: {} {} ", - properties.getTargetPicsureUrl(), pathName, chainRequest.getResourceUUID(), - response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); - httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); - } - - return Response.ok(response.getEntity().getContent()).build(); - } catch (IOException e) { - throw new ApplicationException( - "Error encoding query for resource with id " + queryRequest.getResourceUUID()); - } catch (ClassCastException | IllegalArgumentException e) { - logger.error(e.getMessage()); - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - private Header[] createAuthHeader() { - return new Header[] { - new BasicHeader(HttpHeaders.AUTHORIZATION, BEARER_STRING + properties.getTargetPicsureToken()) }; - } + } + + @POST + @Path("/query/{resourceQueryId}/status") + public QueryStatus queryStatus(@PathParam("resourceQueryId") String queryId, QueryRequest statusRequest) { + // JNix: Retaining for future use... + if (statusRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + + String pathName = "/query/" + queryId + "/status"; + HttpResponse response = null; + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + chainRequest.setQuery(statusRequest.getQuery()); + chainRequest.setResourceCredentials(statusRequest.getResourceCredentials()); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} calling resource with id {} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + chainRequest.getResourceUUID(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + QueryStatus queryStatus = httpClient.readObjectFromResponse(response, QueryStatus.class); + queryStatus.setResourceID(statusRequest.getResourceUUID()); + return queryStatus; + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + statusRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); + } + } + + @POST + @Path("/query/sync") + @Override + public Response querySync(QueryRequest queryRequest) { + if (queryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Object search = queryRequest.getQuery(); + if (search == null) { + throw new ProtocolException((ProtocolException.MISSING_DATA)); + } + + String pathName = "/query/sync"; + + HttpResponse response = null; + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + chainRequest.setQuery(queryRequest.getQuery()); + chainRequest.setResourceCredentials(queryRequest.getResourceCredentials()); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} calling resource with id {} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + chainRequest.getResourceUUID(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + + if (response.containsHeader(QUERY_METADATA_FIELD)) { + Header metadataHeader = ((Header[]) response.getHeaders(QUERY_METADATA_FIELD))[0]; + logger.debug("Found Header[] : " + metadataHeader.getValue()); + return Response.ok(response.getEntity().getContent()).header(QUERY_METADATA_FIELD, metadataHeader.getValue()).build(); + } + + return Response.ok(response.getEntity().getContent()).build(); + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + queryRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); + } + } + + @POST + @Path("/search") + public SearchResults search(QueryRequest searchRequest) { + if (searchRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Object search = searchRequest.getQuery(); + if (search == null) { + throw new ProtocolException((ProtocolException.MISSING_DATA)); + } + + HttpResponse response = null; + String pathName = "/search/" + properties.getTargetResourceId(); + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + chainRequest.setQuery(searchRequest.getQuery()); + chainRequest.setResourceCredentials(searchRequest.getResourceCredentials()); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + return httpClient.readObjectFromResponse(response, SearchResults.class); + } catch (IOException e) { + // Note: this shouldn't ever happen + logger.error("Error encoding search payload", e); + throw new ApplicationException("Error encoding search for resource with id " + searchRequest.getResourceUUID()); + } finally { + closeHttpResponse(response); + } + } + + @Override + public Response queryFormat(QueryRequest queryRequest) { + if (queryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Object search = queryRequest.getQuery(); + if (search == null) { + throw new ProtocolException((ProtocolException.MISSING_DATA)); + } + + String pathName = "/query/format"; + HttpResponse response = null; + try { + QueryRequest chainRequest = new GeneralQueryRequest(); + chainRequest.setQuery(queryRequest.getQuery()); + chainRequest.setResourceCredentials(queryRequest.getResourceCredentials()); + chainRequest.setResourceUUID(UUID.fromString(properties.getTargetResourceId())); + + String payload = objectMapper.writeValueAsString(chainRequest); + response = httpClient + .retrievePostResponse(httpClient.composeURL(properties.getTargetPicsureUrl(), pathName), createAuthHeader(), payload); + if (response.getStatusLine().getStatusCode() != 200) { + logger.error( + "{}{} calling resource with id {} did not return a 200: {} {} ", properties.getTargetPicsureUrl(), pathName, + chainRequest.getResourceUUID(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase() + ); + httpClient.throwInternalResponseError(response, properties.getTargetPicsureUrl()); + } + + return Response.ok(response.getEntity().getContent()).build(); + } catch (IOException e) { + throw new ApplicationException("Error encoding query for resource with id " + queryRequest.getResourceUUID()); + } catch (ClassCastException | IllegalArgumentException e) { + logger.error(e.getMessage()); + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } finally { + closeHttpResponse(response); + } + } + + private Header[] createAuthHeader() { + return new Header[] {new BasicHeader(HttpHeaders.AUTHORIZATION, BEARER_STRING + properties.getTargetPicsureToken())}; + } } diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/test/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRSTest.java b/pic-sure-resources/pic-sure-passthrough-resource/src/test/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRSTest.java index 3505ce69..24d68c50 100644 --- a/pic-sure-resources/pic-sure-passthrough-resource/src/test/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRSTest.java +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/test/java/edu/harvard/hms/dbmi/avillach/resource/passthru/PassThroughResourceRSTest.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.UUID; +import edu.harvard.dbmi.avillach.util.HttpClientUtil; import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HeaderElement; @@ -41,7 +42,7 @@ @ExtendWith(MockitoExtension.class) class PassThroughResourceRSTest { - HttpClient httpClient; + HttpClientUtil httpClient; PassThroughResourceRS resource; ObjectMapper objectMapper = new ObjectMapper(); @@ -52,12 +53,7 @@ void init() { lenient().when(appProperties.getTargetPicsureUrl()).thenReturn("http://test"); lenient().when(appProperties.getTargetResourceId()).thenReturn(UUID.randomUUID().toString()); - httpClient = mock(HttpClient.class); - // not mocking these methods... - lenient().doCallRealMethod().when(httpClient).composeURL(anyString(), anyString()); - lenient().doCallRealMethod().when(httpClient).readObjectFromResponse(any(HttpResponse.class), any()); - lenient().doCallRealMethod().when(httpClient).throwResponseError(any(HttpResponse.class), anyString()); - lenient().doCallRealMethod().when(httpClient).throwInternalResponseError(any(HttpResponse.class), anyString()); + httpClient = mock(HttpClientUtil.class); resource = new PassThroughResourceRS(appProperties, httpClient); } @@ -141,6 +137,7 @@ void testQueryResult() throws Exception { when(httpResponse.getStatusLine()).thenReturn(statusLine); when(httpResponse.getEntity()).thenReturn(httpResponseEntity); when(httpClient.retrievePostResponse(anyString(), any(Header[].class), anyString())).thenReturn(httpResponse); + when(httpClient.readObjectFromResponse(any(HttpResponse.class))).thenReturn("4"); assertThrows(ProtocolException.class, () -> { resource.queryResult("", null); @@ -168,7 +165,7 @@ void testQueryResult() throws Exception { when(httpResponse.getEntity()).thenReturn(httpResponseEntity); GeneralQueryRequest queryRequest = newQueryRequest(null); javax.ws.rs.core.Response returnVal = resource.queryResult(queryId.toString(), queryRequest); - assertEquals("4", IOUtils.toString((InputStream) returnVal.getEntity(), StandardCharsets.UTF_8)); + assertEquals("4", returnVal.getEntity()); //assertEquals(resultId, returnVal.getHeaderString("resultId")); } diff --git a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ProxyWebClient.java b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ProxyWebClient.java index f5025233..17643a3d 100644 --- a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ProxyWebClient.java +++ b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ProxyWebClient.java @@ -11,6 +11,7 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +34,13 @@ public class ProxyWebClient { ResourceRepository resourceRepository; public ProxyWebClient() { - client = HttpClientUtil.getConfiguredHttpClient(); + PoolingHttpClientConnectionManager connectionManager; + + connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); // Maximum total connections + connectionManager.setDefaultMaxPerRoute(20); // Maximum connections per route + + client = HttpClientUtil.getConfiguredHttpClient(connectionManager); } public Response postProxy( diff --git a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ResourceWebClient.java b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ResourceWebClient.java index 09567c41..0ac54062 100644 --- a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ResourceWebClient.java +++ b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/service/ResourceWebClient.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import edu.harvard.dbmi.avillach.domain.*; +import edu.harvard.dbmi.avillach.util.HttpClientUtil; import edu.harvard.dbmi.avillach.util.exception.ApplicationException; import edu.harvard.dbmi.avillach.util.exception.ProtocolException; import edu.harvard.dbmi.avillach.util.exception.ResourceInterfaceException; @@ -11,7 +12,9 @@ import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,54 +25,70 @@ import java.net.URISyntaxException; import java.util.Map; -import static edu.harvard.dbmi.avillach.util.HttpClientUtil.*; +import static edu.harvard.dbmi.avillach.util.HttpClientUtil.closeHttpResponse; /** - * The ResourceWebClient class implements the client side logic for the endpoints specified in IResourceRS. - - The PicsureInfoService, PicsureQueryService and PicsureSearchService would then use this class - to serve calls from their methods to each configured Resource target url - after looking up the target url from the ResourceRepository. + * The ResourceWebClient class implements the client side logic for the endpoints specified in IResourceRS.

The PicsureInfoService, + * PicsureQueryService and PicsureSearchService would then use this class to serve calls from their methods to each configured Resource + * target url after looking up the target url from the ResourceRepository. */ @ApplicationScoped public class ResourceWebClient { - private Logger logger = LoggerFactory.getLogger(this.getClass()); + private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final static ObjectMapper json = new ObjectMapper(); public static final String BEARER_STRING = "Bearer "; public static final String BEARER_TOKEN_KEY = "BEARER_TOKEN"; public static final String QUERY_METADATA_FIELD = "queryMetadata"; - - public ResourceWebClient() { } - public ResourceInfo info(String rsURL, QueryRequest queryRequest){ + private final HttpClientUtil httpClientUtil; + + public ResourceWebClient() { + PoolingHttpClientConnectionManager connectionManager; + + connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); // Maximum total connections + connectionManager.setDefaultMaxPerRoute(20); // Maximum connections per route + + this.httpClientUtil = HttpClientUtil.getInstance(connectionManager); + } + + public ResourceInfo info(String rsURL, QueryRequest queryRequest) { logger.debug("Calling ResourceWebClient info()"); + HttpResponse resourcesResponse = null; try { - if (queryRequest == null){ + if (queryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (queryRequest.getResourceCredentials() == null){ + if (queryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException(NotAuthorizedException.MISSING_CREDENTIALS); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } logger.debug("Calling /info at ResourceURL: {}", rsURL); String pathName = "/info"; String body = json.writeValueAsString(queryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body + ); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return readObjectFromResponse(resourcesResponse, ResourceInfo.class); - } catch (JsonProcessingException e){ + return httpClientUtil.readObjectFromResponse(resourcesResponse, ResourceInfo.class); + } catch (JsonProcessingException e) { throw new NotAuthorizedException("Unable to encode resource credentials", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public PaginatedSearchResult searchConceptValues(String rsURL, QueryRequest queryRequest, String conceptPath, String query, Integer page, Integer size) { + public PaginatedSearchResult searchConceptValues( + String rsURL, QueryRequest queryRequest, String conceptPath, String query, Integer page, Integer size + ) { + HttpResponse resourcesResponse = null; try { logger.debug("Calling /search/values at ResourceURL: {}"); URIBuilder uriBuilder = new URIBuilder(rsURL); @@ -84,210 +103,245 @@ public PaginatedSearchResult searchConceptValues(String rsURL, QueryRequest q uriBuilder.addParameter("size", size.toString()); } Map resourceCredentials = queryRequest != null ? queryRequest.getResourceCredentials() : Map.of(); - HttpResponse resourcesResponse = retrieveGetResponse(uriBuilder.build().toString(), createHeaders(resourceCredentials)); + resourcesResponse = httpClientUtil.retrieveGetResponse(uriBuilder.build().toString(), createHeaders(resourceCredentials)); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return readObjectFromResponse(resourcesResponse, PaginatedSearchResult.class); + return httpClientUtil.readObjectFromResponse(resourcesResponse, PaginatedSearchResult.class); } catch (URISyntaxException e) { throw new ApplicationException("rsURL invalid : " + rsURL, e); + } finally { + closeHttpResponse(resourcesResponse); } } - public SearchResults search(String rsURL, QueryRequest searchQueryRequest){ + public SearchResults search(String rsURL, QueryRequest searchQueryRequest) { logger.debug("Calling ResourceWebClient search()"); + HttpResponse resourcesResponse = null; try { - if (searchQueryRequest == null || searchQueryRequest.getQuery() == null){ + if (searchQueryRequest == null || searchQueryRequest.getQuery() == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } - if (searchQueryRequest.getResourceCredentials() == null){ + if (searchQueryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException(NotAuthorizedException.MISSING_CREDENTIALS); } String pathName = "/search"; String body = json.writeValueAsString(searchQueryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(searchQueryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(searchQueryRequest.getResourceCredentials()), body + ); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return readObjectFromResponse(resourcesResponse, SearchResults.class); - } catch (JsonProcessingException e){ + return httpClientUtil.readObjectFromResponse(resourcesResponse, SearchResults.class); + } catch (JsonProcessingException e) { logger.error("Unable to serialize search query"); - //TODO Write custom exception + // TODO Write custom exception throw new ProtocolException("Unable to serialize search query", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public QueryStatus query(String rsURL, QueryRequest dataQueryRequest){ + public QueryStatus query(String rsURL, QueryRequest dataQueryRequest) { logger.debug("Calling ResourceWebClient query()"); + HttpResponse resourcesResponse = null; try { - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } - if (dataQueryRequest == null){ + if (dataQueryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (dataQueryRequest.getResourceCredentials() == null){ + if (dataQueryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException("Missing credentials"); } String pathName = "/query"; String body = json.writeValueAsString(dataQueryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(dataQueryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(dataQueryRequest.getResourceCredentials()), body + ); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return readObjectFromResponse(resourcesResponse, QueryStatus.class); - } catch (JsonProcessingException e){ + return httpClientUtil.readObjectFromResponse(resourcesResponse, QueryStatus.class); + } catch (JsonProcessingException e) { logger.error("Unable to encode data query"); throw new ProtocolException("Unable to encode data query", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public QueryStatus queryStatus(String rsURL, String queryId, QueryRequest queryRequest){ + public QueryStatus queryStatus(String rsURL, String queryId, QueryRequest queryRequest) { logger.debug("Calling ResourceWebClient query()"); + HttpResponse resourcesResponse = null; try { - if (queryRequest == null){ + if (queryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (queryRequest.getResourceCredentials() == null){ + if (queryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException("Missing credentials"); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } - if (queryId == null){ + if (queryId == null) { throw new ProtocolException("Missing query id"); } String pathName = "/query/" + queryId + "/status"; String body = json.writeValueAsString(queryRequest); - logger.debug(composeURL(rsURL, pathName)); + logger.debug(httpClientUtil.composeURL(rsURL, pathName)); logger.debug(body); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body + ); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return readObjectFromResponse(resourcesResponse, QueryStatus.class); - } catch (JsonProcessingException e){ + return httpClientUtil.readObjectFromResponse(resourcesResponse, QueryStatus.class); + } catch (JsonProcessingException e) { logger.error("Unable to encode resource credentials"); throw new ProtocolException("Unable to encode resource credentials", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public Response queryResult(String rsURL, String queryId, QueryRequest queryRequest){ + public Response queryResult(String rsURL, String queryId, QueryRequest queryRequest) { logger.debug("Calling ResourceWebClient query()"); + + HttpResponse resourcesResponse = null; try { - if (queryRequest == null){ + if (queryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (queryRequest.getResourceCredentials() == null){ + if (queryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException("Missing credentials"); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } - if (queryId == null){ + if (queryId == null) { throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); } String pathName = "/query/" + queryId + "/result"; String body = json.writeValueAsString(queryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + HttpClientUtil.composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body + ); + + String content = httpClientUtil.readObjectFromResponse(resourcesResponse); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + HttpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return Response.ok(resourcesResponse.getEntity().getContent()).build(); - } catch (JsonProcessingException e){ + return Response.ok(content).build(); + } catch (JsonProcessingException e) { logger.error("Unable to encode resource credentials"); throw new NotAuthorizedException("Unable to encode resource credentials", e); - } catch (IOException e){ - throw new ResourceInterfaceException("Error getting results", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public Response queryResultSignedUrl(String rsURL, String queryId, QueryRequest queryRequest){ + public Response queryResultSignedUrl(String rsURL, String queryId, QueryRequest queryRequest) { logger.debug("Calling ResourceWebClient querySignedUrl()"); + HttpResponse resourcesResponse = null; try { - if (queryRequest == null){ + if (queryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (queryRequest.getResourceCredentials() == null){ + if (queryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException("Missing credentials"); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } - if (queryId == null){ + if (queryId == null) { throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); } String pathName = "/query/" + queryId + "/signed-url"; String body = json.writeValueAsString(queryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body + ); + if (resourcesResponse.getStatusLine().getStatusCode() != 200) { logger.error("ResourceRS did not return a 200"); - throwResponseError(resourcesResponse, rsURL); + httpClientUtil.throwResponseError(resourcesResponse, rsURL); } - return Response.ok(resourcesResponse.getEntity().getContent()).build(); - } catch (JsonProcessingException e){ + + String content = httpClientUtil.readObjectFromResponse(resourcesResponse); + return Response.ok(content).build(); + } catch (JsonProcessingException e) { logger.error("Unable to encode resource credentials"); throw new NotAuthorizedException("Unable to encode resource credentials", e); - } catch (IOException e){ - throw new ResourceInterfaceException("Error getting results", e); + } finally { + closeHttpResponse(resourcesResponse); } } - - public Response queryFormat(String rsURL, QueryRequest queryRequest){ + + public Response queryFormat(String rsURL, QueryRequest queryRequest) { logger.debug("Calling ResourceWebClient queryFormat()"); + HttpResponse resourcesResponse = null; try { - if (queryRequest == null){ + if (queryRequest == null) { throw new ProtocolException(ProtocolException.MISSING_DATA); } - if (queryRequest.getResourceCredentials() == null){ + if (queryRequest.getResourceCredentials() == null) { throw new NotAuthorizedException("Missing credentials"); } - if (rsURL == null){ + if (rsURL == null) { throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); } String pathName = "/query/format"; String body = json.writeValueAsString(queryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body); + resourcesResponse = httpClientUtil.retrievePostResponse( + httpClientUtil.composeURL(rsURL, pathName), createHeaders(queryRequest.getResourceCredentials()), body + ); + + String content = httpClientUtil.readObjectFromResponse(resourcesResponse); int status = resourcesResponse.getStatusLine().getStatusCode(); if (status != 200) { - logger.error("Query format request did not return a 200: " + resourcesResponse.getStatusLine().getStatusCode()); - return Response.status(status).entity(resourcesResponse.getEntity().getContent()).build(); + logger.error("Query format request did not return a 200: {}", resourcesResponse.getStatusLine().getStatusCode()); + return Response.status(status).entity(content).build(); } - return Response.ok(resourcesResponse.getEntity().getContent()).build(); - } catch (JsonProcessingException e){ + return Response.ok(content).build(); + } catch (JsonProcessingException e) { logger.error("Unable to encode resource credentials"); throw new NotAuthorizedException("Unable to encode resource credentials", e); - } catch (IOException e){ - throw new ResourceInterfaceException("Error getting results", e); + } finally { + closeHttpResponse(resourcesResponse); } } - public Response querySync(String rsURL, QueryRequest queryRequest, String requestSource) { - logger.debug("Calling ResourceWebClient querySync()"); - try { - if (queryRequest == null) { - throw new ProtocolException("Missing query data"); - } - if (queryRequest.getResourceCredentials() == null) { - throw new NotAuthorizedException("Missing credentials"); - } - if (rsURL == null) { - throw new ApplicationException("Missing resource URL"); - } + public Response querySync(String rsURL, QueryRequest queryRequest, String requestSource) { + logger.debug("Calling ResourceWebClient querySync()"); + HttpResponse resourcesResponse = null; + try { + if (queryRequest == null) { + throw new ProtocolException("Missing query data"); + } + if (queryRequest.getResourceCredentials() == null) { + throw new NotAuthorizedException("Missing credentials"); + } + if (rsURL == null) { + throw new ApplicationException("Missing resource URL"); + } - String pathName = "/query/sync"; - String body = json.writeValueAsString(queryRequest); + String pathName = "/query/sync"; + String body = json.writeValueAsString(queryRequest); Header[] headers = createHeaders(queryRequest.getResourceCredentials()); @@ -301,28 +355,28 @@ public Response querySync(String rsURL, QueryRequest queryRequest, String reques headers = newHeaders; } - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), headers, body); - if (resourcesResponse.getStatusLine().getStatusCode() != 200) { - throwError(resourcesResponse, rsURL); - } - - if (resourcesResponse.containsHeader(QUERY_METADATA_FIELD)) { - Header metadataHeader = ((Header[]) resourcesResponse.getHeaders(QUERY_METADATA_FIELD))[0]; - return Response.ok(resourcesResponse.getEntity().getContent()) - .header(QUERY_METADATA_FIELD, metadataHeader.getValue()).build(); - } - return Response.ok(resourcesResponse.getEntity().getContent()).build(); - } catch (JsonProcessingException e) { - logger.error("Unable to encode resource credentials"); - throw new NotAuthorizedException("Unable to encode resource credentials", e); - } catch (IOException e) { - throw new ResourceInterfaceException("Error getting results", e); - } - } + resourcesResponse = httpClientUtil.retrievePostResponse(httpClientUtil.composeURL(rsURL, pathName), headers, body); + if (resourcesResponse.getStatusLine().getStatusCode() != 200) { + throwError(resourcesResponse, rsURL); + } + + String content = httpClientUtil.readObjectFromResponse(resourcesResponse); + if (resourcesResponse.containsHeader(QUERY_METADATA_FIELD)) { + Header metadataHeader = ((Header[]) resourcesResponse.getHeaders(QUERY_METADATA_FIELD))[0]; + return Response.ok(content).header(QUERY_METADATA_FIELD, metadataHeader.getValue()).build(); + } + return Response.ok(content).build(); + } catch (JsonProcessingException e) { + logger.error("Unable to encode resource credentials"); + throw new NotAuthorizedException("Unable to encode resource credentials", e); + } finally { + closeHttpResponse(resourcesResponse); + } + } /** - * This method is used to call the /bin/continuous endpoint on the ResourceRS. The /bin/continuous endpoint is used - * to retrieve binned continuous data from the visualization resource. + * This method is used to call the /bin/continuous endpoint on the ResourceRS. The /bin/continuous endpoint is used to retrieve binned + * continuous data from the visualization resource. * * @param rsURL The URL of the ResourceRS * @param queryRequest The query request object @@ -331,6 +385,7 @@ public Response querySync(String rsURL, QueryRequest queryRequest, String reques */ public Response queryContinuous(String rsURL, QueryRequest queryRequest, String requestSource) { logger.debug("Calling ResourceWebClient queryContinuous()"); + HttpResponse resourcesResponse = null; try { if (queryRequest == null) { throw new ProtocolException("Missing query data"); @@ -357,28 +412,30 @@ public Response queryContinuous(String rsURL, QueryRequest queryRequest, String } logger.debug("Calling ResourceWebClient queryContinuous() with body: " + body + " and headers: " + queryRequest); - HttpResponse resourcesResponse = retrievePostResponse(composeURL(rsURL, pathName), headers, body); + resourcesResponse = httpClientUtil.retrievePostResponse(HttpClientUtil.composeURL(rsURL, pathName), headers, body); if (resourcesResponse.getStatusLine().getStatusCode() != 200) { throwError(resourcesResponse, rsURL); } - return Response.ok(resourcesResponse.getEntity().getContent()).build(); + String content = httpClientUtil.readObjectFromResponse(resourcesResponse); + return Response.ok(content).build(); } catch (JsonProcessingException e) { throw new RuntimeException(e); - } catch (IOException e) { - throw new ResourceInterfaceException("Error getting results", e); + } finally { + closeHttpResponse(resourcesResponse); } } - private void throwError(HttpResponse response, String baseURL){ + private void throwError(HttpResponse response, String baseURL) { logger.error("ResourceRS did not return a 200"); String errorMessage = baseURL + " " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase(); try { JsonNode responseNode = json.readTree(response.getEntity().getContent()); - if (responseNode != null && responseNode.has("message")){ + if (responseNode != null && responseNode.has("message")) { errorMessage += "/n" + responseNode.get("message").asText(); } - } catch (IOException e ){ + } catch (IOException e) { + logger.error(e.getMessage()); } if (response.getStatusLine().getStatusCode() == 401) { throw new NotAuthorizedException(errorMessage); @@ -387,7 +444,7 @@ private void throwError(HttpResponse response, String baseURL){ } - private Header[] createHeaders(Map resourceCredentials){ + private Header[] createHeaders(Map resourceCredentials) { Header authorizationHeader = new BasicHeader(HttpHeaders.AUTHORIZATION, BEARER_STRING + resourceCredentials.get(BEARER_TOKEN_KEY)); Header contentTypeHeader = new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json"); Header[] headers = {authorizationHeader, contentTypeHeader}; diff --git a/pic-sure-resources/pic-sure-resource-api/src/test/java/edu/harvard/dbmi/avillach/service/ResourceWebClientTest.java b/pic-sure-resources/pic-sure-resource-api/src/test/java/edu/harvard/dbmi/avillach/service/ResourceWebClientTest.java index bac8e462..6ed3c094 100644 --- a/pic-sure-resources/pic-sure-resource-api/src/test/java/edu/harvard/dbmi/avillach/service/ResourceWebClientTest.java +++ b/pic-sure-resources/pic-sure-resource-api/src/test/java/edu/harvard/dbmi/avillach/service/ResourceWebClientTest.java @@ -8,6 +8,8 @@ import edu.harvard.dbmi.avillach.util.exception.ApplicationException; import edu.harvard.dbmi.avillach.util.exception.ProtocolException; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.util.EntityUtils; import org.glassfish.jersey.internal.RuntimeDelegateImpl; import org.junit.BeforeClass; import org.junit.Rule; @@ -33,7 +35,7 @@ public class ResourceWebClientTest { private final static ObjectMapper json = new ObjectMapper(); private final static String token = "testToken"; private final static int port = 8079; - private final static String testURL = "http://localhost:"+port; + private final static String testURL = "http://localhost:" + port; private final ResourceWebClient cut = new ResourceWebClient(); @Rule @@ -42,23 +44,20 @@ public class ResourceWebClientTest { @BeforeClass public static void beforeClass() { - //Need to be able to throw exceptions without container so we can verify correct errors are being thrown + // Need to be able to throw exceptions without container so we can verify correct errors are being thrown RuntimeDelegate runtimeDelegate = new RuntimeDelegateImpl(); RuntimeDelegate.setInstance(runtimeDelegate); } @Test - public void testInfo() throws JsonProcessingException{ + public void testInfo() throws JsonProcessingException { String resourceInfo = json.writeValueAsString(new ResourceInfo()); - wireMockRule.stubFor(any(urlEqualTo("/info")) - .willReturn(aResponse() - .withStatus(200) - .withBody(resourceInfo))); + wireMockRule.stubFor(any(urlEqualTo("/info")).willReturn(aResponse().withStatus(200).withBody(resourceInfo))); - //Any targetURL that matches /info will trigger wiremock + // Any targetURL that matches /info will trigger wiremock String targetURL = "/info"; - //Should throw an error if any parameters are missing + // Should throw an error if any parameters are missing try { cut.info(testURL, null); fail(); @@ -69,8 +68,8 @@ public void testInfo() throws JsonProcessingException{ Map credentials = new HashMap<>(); credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); queryRequest.setResourceCredentials(credentials); -// queryRequest.setTargetURL(targetURL); - //Obviously should fail without the rsURL + // queryRequest.setTargetURL(targetURL); + // Obviously should fail without the rsURL try { cut.info(null, queryRequest); fail(); @@ -78,58 +77,50 @@ public void testInfo() throws JsonProcessingException{ assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); } - //Should fail without a targetURL + // Should fail without a targetURL -// queryRequest.setTargetURL(null); -// try { -// cut.info(testURL, queryRequest); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } + // queryRequest.setTargetURL(null); + // try { + // cut.info(testURL, queryRequest); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } - //Assuming everything goes right -// queryRequest.setTargetURL(targetURL); + // Assuming everything goes right + // queryRequest.setTargetURL(targetURL); ResourceInfo result = cut.info(testURL, queryRequest); assertNotNull("Result should not be null", result); - //What if the resource has a problem? - wireMockRule.stubFor(any(urlEqualTo("/info")) - .willReturn(aResponse() - .withStatus(500))); + // What if the resource has a problem? + wireMockRule.stubFor(any(urlEqualTo("/info")).willReturn(aResponse().withStatus(500))); try { cut.info(testURL, queryRequest); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("500 Server Error")); + assertTrue(e.getMessage().contains("500 Server Error")); } - //What if resource returns the wrong type of object for some reason? + // What if resource returns the wrong type of object for some reason? String incorrectResponse = json.writeValueAsString(new SearchResults()); - wireMockRule.stubFor(any(urlEqualTo("/info")) - .willReturn(aResponse() - .withStatus(200) - .withBody(incorrectResponse))); + wireMockRule.stubFor(any(urlEqualTo("/info")).willReturn(aResponse().withStatus(200).withBody(incorrectResponse))); try { cut.info(testURL, queryRequest); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("Incorrect object type returned")); + assertTrue(e.getMessage().contains("Incorrect object type returned")); } } @Test - public void testSearch() throws JsonProcessingException{ + public void testSearch() throws JsonProcessingException { String searchResults = json.writeValueAsString(new SearchResults()); - wireMockRule.stubFor(any(urlEqualTo("/search")) - .willReturn(aResponse() - .withStatus(200) - .withBody(searchResults))); + wireMockRule.stubFor(any(urlEqualTo("/search")).willReturn(aResponse().withStatus(200).withBody(searchResults))); - //Should throw an error if any parameters are missing + // Should throw an error if any parameters are missing try { cut.search(testURL, null); fail(); @@ -147,15 +138,15 @@ public void testSearch() throws JsonProcessingException{ request.setQuery("query"); -// try { -// cut.search(testURL, request); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } + // try { + // cut.search(testURL, request); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } String targetURL = "/search"; -// request.setTargetURL(targetURL); + // request.setTargetURL(targetURL); try { cut.search(null, request); @@ -164,84 +155,73 @@ public void testSearch() throws JsonProcessingException{ assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); } -// //Should fail if no credentials given -// request.setQuery("query"); -// request.setTargetURL(targetURL); -// try { -// cut.search(testURL, request); -// fail(); -// } catch (Exception e) { -// assertEquals("HTTP 401 Unauthorized", e.getMessage()); -// } - - //With credentials but not search term - /* Map credentials = new HashMap<>(); - credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); - request.setQuery(null); - request.setResourceCredentials(credentials); - try { - cut.search(testURL, request); - fail(); - } catch (ProtocolException e) { - assertEquals(ProtocolException.MISSING_DATA, e.getContent()); - }*/ - - //Should fail with no targetURL + // //Should fail if no credentials given + // request.setQuery("query"); + // request.setTargetURL(targetURL); + // try { + // cut.search(testURL, request); + // fail(); + // } catch (Exception e) { + // assertEquals("HTTP 401 Unauthorized", e.getMessage()); + // } + + // With credentials but not search term + /* + * Map credentials = new HashMap<>(); credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); + * request.setQuery(null); request.setResourceCredentials(credentials); try { cut.search(testURL, request); fail(); } catch + * (ProtocolException e) { assertEquals(ProtocolException.MISSING_DATA, e.getContent()); } + */ + + // Should fail with no targetURL Map credentials = new HashMap<>(); credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); request.setResourceCredentials(credentials); -// request.setTargetURL(null); + // request.setTargetURL(null); request.setQuery("%blood%"); -// try { -// cut.search(testURL, request); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } - -// request.setTargetURL(targetURL); + // try { + // cut.search(testURL, request); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } + + // request.setTargetURL(targetURL); SearchResults result = cut.search(testURL, request); assertNotNull("Result should not be null", result); - //What if the resource has a problem? - wireMockRule.stubFor(any(urlEqualTo("/search")) - .willReturn(aResponse() - .withStatus(500))); + // What if the resource has a problem? + wireMockRule.stubFor(any(urlEqualTo("/search")).willReturn(aResponse().withStatus(500))); try { cut.search(testURL, request); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("500 Server Error")); + assertTrue(e.getMessage().contains("500 Server Error")); } - //What if resource returns the wrong type of object for some reason? + // What if resource returns the wrong type of object for some reason? ResourceInfo incorrectResponse = new ResourceInfo(); incorrectResponse.setName("resource name"); incorrectResponse.setId(new UUID(1L, 1L)); - wireMockRule.stubFor(any(urlEqualTo("/search")) - .willReturn(aResponse() - .withStatus(200) - .withBody(json.writeValueAsString(incorrectResponse)))); + wireMockRule.stubFor( + any(urlEqualTo("/search")).willReturn(aResponse().withStatus(200).withBody(json.writeValueAsString(incorrectResponse))) + ); try { cut.search(testURL, request); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("Incorrect object type returned")); + assertTrue(e.getMessage().contains("Incorrect object type returned")); } } @Test - public void testQuery() throws JsonProcessingException{ + public void testQuery() throws JsonProcessingException { String queryResults = json.writeValueAsString(new QueryStatus()); - wireMockRule.stubFor(any(urlEqualTo("/query")) - .willReturn(aResponse() - .withStatus(200) - .withBody(queryResults))); + wireMockRule.stubFor(any(urlEqualTo("/query")).willReturn(aResponse().withStatus(200).withBody(queryResults))); - //Should fail if any parameters are missing + // Should fail if any parameters are missing try { cut.query(testURL, null); fail(); @@ -249,7 +229,7 @@ public void testQuery() throws JsonProcessingException{ assertEquals(ProtocolException.MISSING_DATA, e.getContent()); } GeneralQueryRequest request = new GeneralQueryRequest(); -// request.setTargetURL("/query"); + // request.setTargetURL("/query"); try { cut.query(null, request); @@ -258,148 +238,134 @@ public void testQuery() throws JsonProcessingException{ assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); } - //Should fail if no credentials given -// try { -// cut.query(testURL, request); -// fail(); -// } catch (Exception e) { -// assertEquals("HTTP 401 Unauthorized", e.getMessage()); -// } + // Should fail if no credentials given + // try { + // cut.query(testURL, request); + // fail(); + // } catch (Exception e) { + // assertEquals("HTTP 401 Unauthorized", e.getMessage()); + // } Map credentials = new HashMap<>(); request.setResourceCredentials(credentials); -// request.setTargetURL(null); - //Should fail without a targetURL -// try { -// cut.query(testURL, request); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } - -// request.setTargetURL("/query"); - - //Everything goes correctly + // request.setTargetURL(null); + // Should fail without a targetURL + // try { + // cut.query(testURL, request); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } + + // request.setTargetURL("/query"); + + // Everything goes correctly QueryStatus result = cut.query(testURL, request); assertNotNull("Result should not be null", result); - //What if the resource has a problem? - wireMockRule.stubFor(any(urlEqualTo("/query")) - .willReturn(aResponse() - .withStatus(500))); + // What if the resource has a problem? + wireMockRule.stubFor(any(urlEqualTo("/query")).willReturn(aResponse().withStatus(500))); try { cut.query(testURL, request); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("500 Server Error")); + assertTrue(e.getMessage().contains("500 Server Error")); } - //What if resource returns the wrong type of object for some reason? + // What if resource returns the wrong type of object for some reason? ResourceInfo incorrectResponse = new ResourceInfo(); incorrectResponse.setName("resource name"); incorrectResponse.setId(new UUID(1L, 1L)); - wireMockRule.stubFor(any(urlEqualTo("/query")) - .willReturn(aResponse() - .withStatus(200) - .withBody(json.writeValueAsString(incorrectResponse)))); + wireMockRule.stubFor( + any(urlEqualTo("/query")).willReturn(aResponse().withStatus(200).withBody(json.writeValueAsString(incorrectResponse))) + ); try { cut.query(testURL, request); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("Incorrect object type returned")); + assertTrue(e.getMessage().contains("Incorrect object type returned")); } } @Test - public void testQueryResult() throws JsonProcessingException{ + public void testQueryResult() throws JsonProcessingException { String testId = "230048"; - String mockResult = "Any old response will work"; + String mockResult = "{}"; - wireMockRule.stubFor(any(urlMatching("/query/.*/result")) - .willReturn(aResponse() - .withStatus(200) - .withBody(mockResult))); + wireMockRule.stubFor(any(urlMatching("/query/.*/result")).willReturn(aResponse().withStatus(200).withBody(mockResult))); - //Should fail if missing any parameters -// try { -// cut.queryResult(testURL, testId, null); -// fail(); -// } catch (ProtocolException e) { -// assertEquals(ProtocolException.MISSING_DATA, e.getContent()); -// } + // Should fail if missing any parameters + // try { + // cut.queryResult(testURL, testId, null); + // fail(); + // } catch (ProtocolException e) { + // assertEquals(ProtocolException.MISSING_DATA, e.getContent()); + // } GeneralQueryRequest queryRequest = new GeneralQueryRequest(); Map credentials = new HashMap<>(); credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); queryRequest.setResourceCredentials(credentials); -// String targetURL = "/query/13452134/result"; -//// queryRequest.setTargetURL(targetURL); -// -// try { -// cut.queryResult(testURL, null, queryRequest); -// fail(); -// } catch (ProtocolException e) { -// assertEquals(ProtocolException.MISSING_QUERY_ID, e.getContent()); -// } -// try { -// cut.queryResult(null, testId, queryRequest); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); -// } - -//// queryRequest.setTargetURL(null); -// //Should fail without a targetURL -// try { -// cut.queryResult(testURL, testId, queryRequest); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } -// -//// queryRequest.setTargetURL(targetURL); - - - //Everything should work here - Response result = cut.queryResult(testURL,testId, queryRequest); + // String targetURL = "/query/13452134/result"; + //// queryRequest.setTargetURL(targetURL); + // + // try { + // cut.queryResult(testURL, null, queryRequest); + // fail(); + // } catch (ProtocolException e) { + // assertEquals(ProtocolException.MISSING_QUERY_ID, e.getContent()); + // } + // try { + // cut.queryResult(null, testId, queryRequest); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); + // } + + //// queryRequest.setTargetURL(null); + // //Should fail without a targetURL + // try { + // cut.queryResult(testURL, testId, queryRequest); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } + // + //// queryRequest.setTargetURL(targetURL); + + + // Everything should work here + Response result = cut.queryResult(testURL, testId, queryRequest); assertNotNull("Result should not be null", result); - try { - String resultContent = IOUtils.toString((InputStream) result.getEntity(), "UTF-8"); - assertEquals("Result should match " + mockResult, mockResult, resultContent); - } catch (IOException e ){ - fail("Result content was unreadable"); - } + // String resultContent = IOUtils.toString((InputStream) result.getEntity(), "UTF-8"); + String resultContent = (String) result.getEntity(); + assertEquals("Result should match " + mockResult, mockResult, resultContent); - //What if the resource has a problem? - wireMockRule.stubFor(any(urlMatching("/query/.*/result")) - .willReturn(aResponse() - .withStatus(500))); + // What if the resource has a problem? + wireMockRule.stubFor(any(urlMatching("/query/.*/result")).willReturn(aResponse().withStatus(500))); try { cut.queryResult(testURL, testId, queryRequest); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("500 Server Error")); + assertTrue(e.getMessage().contains("500 Server Error")); } } @Test - public void testQueryStatus() throws JsonProcessingException{ + public void testQueryStatus() throws JsonProcessingException { String testId = "230048"; QueryStatus testResult = new QueryStatus(); testResult.setStatus(PicSureStatus.PENDING); testResult.setResourceStatus("RUNNING"); String queryStatus = json.writeValueAsString(testResult); - wireMockRule.stubFor(any(urlMatching("/query/.*/status")) - .willReturn(aResponse() - .withStatus(200) - .withBody(queryStatus))); + wireMockRule.stubFor(any(urlMatching("/query/.*/status")).willReturn(aResponse().withStatus(200).withBody(queryStatus))); - //Fails with any missing parameters + // Fails with any missing parameters try { cut.queryStatus(testURL, testId, null); fail(); @@ -410,8 +376,8 @@ public void testQueryStatus() throws JsonProcessingException{ Map credentials = new HashMap<>(); credentials.put(ResourceWebClient.BEARER_TOKEN_KEY, token); queryRequest.setResourceCredentials(credentials); -// String targetURL = "/query/13452134/result"; -// queryRequest.setTargetURL(targetURL); + // String targetURL = "/query/13452134/result"; + // queryRequest.setTargetURL(targetURL); try { cut.queryStatus(testURL, null, queryRequest); @@ -426,55 +392,52 @@ public void testQueryStatus() throws JsonProcessingException{ assertEquals(ApplicationException.MISSING_RESOURCE_PATH, e.getContent()); } -// queryRequest.setTargetURL(null); + // queryRequest.setTargetURL(null); - //Should fail without a targetURL -// try { -// cut.queryStatus(testURL, testId, queryRequest); -// fail(); -// } catch (ApplicationException e) { -// assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); -// } + // Should fail without a targetURL + // try { + // cut.queryStatus(testURL, testId, queryRequest); + // fail(); + // } catch (ApplicationException e) { + // assertEquals(ApplicationException.MISSING_TARGET_URL, e.getContent()); + // } -// queryRequest.setTargetURL(targetURL); + // queryRequest.setTargetURL(targetURL); - //Everything should work here - QueryStatus result = cut.queryStatus(testURL,testId, queryRequest); + // Everything should work here + QueryStatus result = cut.queryStatus(testURL, testId, queryRequest); assertNotNull("Result should not be null", result); - //Make sure all necessary fields are present - assertNotNull("Duration should not be null",result.getDuration()); - assertNotNull("Expiration should not be null",result.getExpiration()); - assertNotNull("ResourceStatus should not be null",result.getResourceStatus()); - assertNotNull("Status should not be null",result.getStatus()); + // Make sure all necessary fields are present + assertNotNull("Duration should not be null", result.getDuration()); + assertNotNull("Expiration should not be null", result.getExpiration()); + assertNotNull("ResourceStatus should not be null", result.getResourceStatus()); + assertNotNull("Status should not be null", result.getStatus()); - //What if the resource has a problem? - wireMockRule.stubFor(any(urlMatching("/query/.*/status")) - .willReturn(aResponse() - .withStatus(500))); + // What if the resource has a problem? + wireMockRule.stubFor(any(urlMatching("/query/.*/status")).willReturn(aResponse().withStatus(500))); try { cut.queryStatus(testURL, testId, queryRequest); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("500 Server Error")); + assertTrue(e.getMessage().contains("500 Server Error")); } - //What if resource returns the wrong type of object for some reason? + // What if resource returns the wrong type of object for some reason? ResourceInfo incorrect = new ResourceInfo(); incorrect.setName("resource name"); incorrect.setId(new UUID(1L, 1L)); - wireMockRule.stubFor(any(urlMatching("/query/.*/status")) - .willReturn(aResponse() - .withStatus(200) - .withBody(json.writeValueAsString(incorrect)))); + wireMockRule.stubFor( + any(urlMatching("/query/.*/status")).willReturn(aResponse().withStatus(200).withBody(json.writeValueAsString(incorrect))) + ); try { cut.queryStatus(testURL, testId, queryRequest); fail(); } catch (Exception e) { - assertTrue( e.getMessage().contains("Incorrect object type returned")); + assertTrue(e.getMessage().contains("Incorrect object type returned")); } } } diff --git a/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java b/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java index 79d8c52f..b4f1fb2a 100644 --- a/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java +++ b/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java @@ -11,7 +11,6 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.*; -import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -27,12 +26,14 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,12 +49,14 @@ public class HttpClientUtil { private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); - public static boolean is2xx(HttpResponse response) { - return response.getStatusLine().getStatusCode() / 100 == 2; + private final HttpClient httpClient; + + public HttpClientUtil(HttpClient httpClient) { + this.httpClient = httpClient; } - public static HttpResponse retrieveGetResponse(String uri, List

headers) { - return retrieveGetResponse(uri, headers.toArray(new Header[headers.size()])); + public static boolean is2xx(HttpResponse response) { + return response.getStatusLine().getStatusCode() / 100 == 2; } /** @@ -63,12 +66,12 @@ public static HttpResponse retrieveGetResponse(String uri, List
headers) * @param headers * @return */ - public static HttpResponse retrieveGetResponse(String uri, Header[] headers) { + public HttpResponse retrieveGetResponse(String uri, Header[] headers) { try { logger.debug("HttpClientUtil retrieveGetResponse()"); - HttpClient client = getConfiguredHttpClient(); - return simpleGet(client, uri, headers); + //HttpClient client = getConfiguredHttpClient(); + return simpleGet(httpClient, uri, headers); } catch (ApplicationException e) { throw new ResourceInterfaceException(uri, e); } @@ -105,7 +108,7 @@ public static String composeURL(String baseURL, String pathName, String query) { * @param headers * @return */ - public static HttpResponse retrievePostResponse(String uri, Header[] headers, String body) { + public HttpResponse retrievePostResponse(String uri, Header[] headers, String body) { try { logger.debug("HttpClientUtil retrievePostResponse()"); @@ -115,21 +118,21 @@ public static HttpResponse retrievePostResponse(String uri, Header[] headers, St headerList = new ArrayList<>(Arrays.asList(headers)); headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); - HttpClient client = getConfiguredHttpClient(); - return simplePost(uri, client, new StringEntity(body), headerList.toArray(new Header[headerList.size()])); + //HttpClient client = getConfiguredHttpClient(); + return simplePost(uri, httpClient, new StringEntity(body), headerList.toArray(new Header[headerList.size()])); } catch (ApplicationException | UnsupportedEncodingException e) { throw new ResourceInterfaceException(uri, e); } } - public static HttpResponse retrievePostResponse(String uri, List
headers, String body) { + public HttpResponse retrievePostResponse(String uri, List
headers, String body) { return retrievePostResponse(uri, headers.toArray(new Header[headers.size()]), body); } - public static List readListFromResponse(HttpResponse response, Class expectedElementType) { + public List readListFromResponse(HttpResponse response, Class expectedElementType) { logger.debug("HttpClientUtil readListFromResponse()"); try { - String responseBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); return json.readValue(responseBody, new TypeReference>() { }); } catch (IOException e) { @@ -137,10 +140,10 @@ public static List readListFromResponse(HttpResponse response, Class e } } - public static String readObjectFromResponse(HttpResponse response) { + public String readObjectFromResponse(HttpResponse response) { logger.debug("HttpClientUtil readObjectFromResponse(HttpResponse response)"); try { - String responseBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); logger.debug("readObjectFromResponse() responseBody {}", responseBody); return responseBody; } catch (IOException e) { @@ -152,9 +155,9 @@ public static T readObjectFromResponse(HttpResponse response, Class expec logger.debug("HttpClientUtil readObjectFromResponse()"); try { long startTime = System.nanoTime(); - String responseBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); logger.debug( - "readObjectFromResponse() line: IOUtils.toString(response.getEntity().getContent(), \"UTF-8\"), took {}", + "readObjectFromResponse() line: EntityUtils.toString(response.getEntity().getContent(), \"UTF-8\"), took {}", (System.nanoTime() - startTime)); logger.trace("readObjectFromResponse() responseBody {}", responseBody); @@ -212,18 +215,15 @@ public static void throwInternalResponseError(HttpResponse response, String base * @return HttpResponse * @throws ApplicationException */ - public static HttpResponse simplePost(String uri, HttpClient client, StringEntity requestBody, Header... headers) + public HttpResponse simplePost(String uri, HttpClient client, StringEntity requestBody, Header... headers) throws ApplicationException { - if (client == null) { - client = getConfiguredHttpClient(); - } HttpPost post = new HttpPost(uri); post.setHeaders(headers); post.setEntity(requestBody); try { - return client.execute(post, buildHttpClientContext()); + return httpClient.execute(post, buildHttpClientContext()); } catch (IOException ex) { logger.error("simplePost() Exception: {}, cannot get response by POST from url: {}", ex.getMessage(), uri); throw new ApplicationException("Inner problem, please contact system admin and check the server log"); @@ -239,7 +239,7 @@ public static HttpResponse simplePost(String uri, HttpClient client, StringEntit * @param headers * @return InputStream */ - public static InputStream simplePost(String uri, StringEntity requestBody, HttpClient client, Header... headers) { + public InputStream simplePost(String uri, StringEntity requestBody, HttpClient client, Header... headers) { HttpResponse response = simplePost(uri, client, requestBody, headers); try { @@ -250,27 +250,6 @@ public static InputStream simplePost(String uri, StringEntity requestBody, HttpC } } - /** - * only works if the POST method returns a JSON response body - * - * @param uri - * @param requestBody - * @param client - * @param objectMapper - * @param headers - * @return - */ - public static JsonNode simplePost(String uri, StringEntity requestBody, HttpClient client, - ObjectMapper objectMapper, Header... headers) { - try { - return objectMapper.readTree(simplePost(uri, requestBody, client, headers)); - } catch (IOException ex) { - logger.error("simplePost() Exception: {}, cannot parse content from by POST from url: {}", ex.getMessage(), - uri); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - /** * for general and basic use of GET function using Apache Http Client * @@ -280,27 +259,23 @@ public static JsonNode simplePost(String uri, StringEntity requestBody, HttpClie * @return * @throws ApplicationException */ - public static HttpResponse simpleGet(HttpClient client, String uri, Header... headers) throws ApplicationException { - if (client == null) { - client = getConfiguredHttpClient(); - } - + public HttpResponse simpleGet(HttpClient client, String uri, Header... headers) throws ApplicationException { HttpGet get = new HttpGet(uri); get.setHeaders(headers); try { - return client.execute(get, buildHttpClientContext()); + return httpClient.execute(get, buildHttpClientContext()); } catch (IOException ex) { logger.error("HttpResponse simpleGet() cannot get response by GET from url: {} - " + ex.getLocalizedMessage(), uri); throw new ApplicationException("Inner problem, please contact system admin and check the server log"); } } - public static InputStream simpleGet(String uri, HttpClient client, Header... headers) { + public InputStream simpleGet(String uri, HttpClient client, Header... headers) { return simpleGetWithConfig(uri, client, null, headers); } - public static InputStream simpleGetWithConfig( + public InputStream simpleGetWithConfig( String uri, HttpClient client, RequestConfig config, Header... headers ) throws ApplicationException { HttpGet get = new HttpGet(uri); @@ -310,7 +285,7 @@ public static InputStream simpleGetWithConfig( } try { - return client.execute(get, buildHttpClientContext()) + return httpClient.execute(get, buildHttpClientContext()) .getEntity() .getContent(); } catch (IOException ex) { @@ -318,38 +293,15 @@ public static InputStream simpleGetWithConfig( throw new ApplicationException("Inner problem, please contact system admin and check the server log"); } } - - /** - * only work if the GET method returns a JSON response body - * - * @param uri - * @param client - * @param objectMapper - * @param headers - * @return - */ - public static JsonNode simpleGet(String uri, HttpClient client, ObjectMapper objectMapper, Header... headers) { - try { - return objectMapper.readTree(simpleGet(uri, client, headers)); - } catch (IOException ex) { - logger.error("simpleGet() cannot parse content from by GET from url: {}", uri, ex); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - - public static JsonNode simpleGetWithConfig( - String uri, HttpClient client, ObjectMapper objectMapper, RequestConfig requestConfig, Header... headers - ) { - try { - return objectMapper.readTree(simpleGetWithConfig(uri, client, requestConfig, headers)); - } catch (IOException ex) { - logger.error("simpleGet() cannot parse content from by GET from url: {}", uri, ex); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - public static HttpClient getConfiguredHttpClient() { + public static HttpClient getConfiguredHttpClient(HttpClientConnectionManager connectionManager) { try { + int timeout = 30; + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(timeout * 1000) + .setConnectionRequestTimeout(timeout * 1000) + .setSocketTimeout(timeout * 1000).build(); + SSLConnectionSocketFactory.getSocketFactory(); SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, null, null); @@ -364,13 +316,15 @@ public static HttpClient getConfiguredHttpClient() { limited.add(suite); } } - + return HttpClients.custom() .setSSLSocketFactory(new SSLConnectionSocketFactory( SSLContexts.createSystemDefault(), new String[]{"TLSv1.2"}, limited.toArray(new String[limited.size()]), SSLConnectionSocketFactory.getDefaultHostnameVerifier())) + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(config) .build(); } catch( NoSuchAlgorithmException | KeyManagementException e) { logger.warn("Unable to establish SSL context. using default client", e); @@ -379,4 +333,18 @@ public static HttpClient getConfiguredHttpClient() { //default return HttpClientBuilder.create().useSystemProperties().build(); } + + public static HttpClientUtil getInstance(HttpClientConnectionManager connectionManager) { + return new HttpClientUtil(getConfiguredHttpClient(connectionManager)); + } + + public static void closeHttpResponse(HttpResponse resourcesResponse) { + if (resourcesResponse != null) { + try { + EntityUtils.consume(resourcesResponse.getEntity()); + } catch (IOException e) { + logger.error("Failed to close HttpResponse entity", e); + } + } + } }