001package org.hl7.fhir.dstu2016may.utils.client; 002 003 004 005 006 007/* 008 Copyright (c) 2011+, HL7, Inc. 009 All rights reserved. 010 011 Redistribution and use in source and binary forms, with or without modification, 012 are permitted provided that the following conditions are met: 013 014 * Redistributions of source code must retain the above copyright notice, this 015 list of conditions and the following disclaimer. 016 * Redistributions in binary form must reproduce the above copyright notice, 017 this list of conditions and the following disclaimer in the documentation 018 and/or other materials provided with the distribution. 019 * Neither the name of HL7 nor the names of its contributors may be used to 020 endorse or promote products derived from this software without specific 021 prior written permission. 022 023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 026 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 027 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 028 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 029 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 030 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 031 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 032 POSSIBILITY OF SUCH DAMAGE. 033 034*/ 035 036 037import java.io.ByteArrayOutputStream; 038import java.io.IOException; 039import java.io.InputStream; 040import java.io.OutputStreamWriter; 041import java.io.UnsupportedEncodingException; 042import java.net.HttpURLConnection; 043import java.net.MalformedURLException; 044import java.net.URI; 045import java.net.URLConnection; 046import java.nio.charset.StandardCharsets; 047import java.text.ParseException; 048import java.text.SimpleDateFormat; 049import java.util.ArrayList; 050import java.util.Calendar; 051import java.util.Date; 052import java.util.List; 053import java.util.Locale; 054import java.util.Map; 055 056import org.apache.commons.codec.binary.Base64; 057import org.apache.commons.io.IOUtils; 058import org.apache.commons.lang3.StringUtils; 059import org.apache.http.Header; 060import org.apache.http.HttpEntityEnclosingRequest; 061import org.apache.http.HttpHost; 062import org.apache.http.HttpRequest; 063import org.apache.http.HttpResponse; 064import org.apache.http.client.HttpClient; 065import org.apache.http.client.methods.HttpDelete; 066import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 067import org.apache.http.client.methods.HttpGet; 068import org.apache.http.client.methods.HttpOptions; 069import org.apache.http.client.methods.HttpPost; 070import org.apache.http.client.methods.HttpPut; 071import org.apache.http.client.methods.HttpUriRequest; 072import org.apache.http.conn.params.ConnRoutePNames; 073import org.apache.http.entity.ByteArrayEntity; 074import org.apache.http.impl.client.DefaultHttpClient; 075import org.apache.http.params.HttpConnectionParams; 076import org.apache.http.params.HttpParams; 077import org.hl7.fhir.dstu2016may.formats.IParser; 078import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle; 079import org.hl7.fhir.dstu2016may.formats.JsonParser; 080import org.hl7.fhir.dstu2016may.formats.XmlParser; 081import org.hl7.fhir.dstu2016may.model.Bundle; 082import org.hl7.fhir.dstu2016may.model.OperationOutcome; 083import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity; 084import org.hl7.fhir.dstu2016may.model.OperationOutcome.OperationOutcomeIssueComponent; 085import org.hl7.fhir.dstu2016may.model.Resource; 086import org.hl7.fhir.dstu2016may.model.ResourceType; 087import org.hl7.fhir.dstu2016may.utils.ResourceUtilities; 088import org.hl7.fhir.utilities.ToolingClientLogger; 089import org.hl7.fhir.utilities.Utilities; 090 091/** 092 * Helper class handling lower level HTTP transport concerns. 093 * TODO Document methods. 094 * @author Claude Nanjo 095 */ 096public class ClientUtils { 097 098 public static final String DEFAULT_CHARSET = "UTF-8"; 099 public static final String HEADER_LOCATION = "location"; 100 private static boolean debugging = false; 101 102 private HttpHost proxy; 103 private int timeout = 5000; 104 private String username; 105 private String password; 106 private ToolingClientLogger logger; 107 private int retryCount; 108 109 public HttpHost getProxy() { 110 return proxy; 111 } 112 113 public void setProxy(HttpHost proxy) { 114 this.proxy = proxy; 115 } 116 117 public int getTimeout() { 118 return timeout; 119 } 120 121 public void setTimeout(int timeout) { 122 this.timeout = timeout; 123 } 124 125 public String getUsername() { 126 return username; 127 } 128 129 public void setUsername(String username) { 130 this.username = username; 131 } 132 133 public String getPassword() { 134 return password; 135 } 136 137 public void setPassword(String password) { 138 this.password = password; 139 } 140 141 public <T extends Resource> ResourceRequest<T> issueOptionsRequest(URI optionsUri, String resourceFormat, int timeoutLoading) { 142 HttpOptions options = new HttpOptions(optionsUri); 143 return issueResourceRequest(resourceFormat, options, timeoutLoading); 144 } 145 146 public <T extends Resource> ResourceRequest<T> issueGetResourceRequest(URI resourceUri, String resourceFormat, int timeoutLoading) { 147 HttpGet httpget = new HttpGet(resourceUri); 148 return issueResourceRequest(resourceFormat, httpget, timeoutLoading); 149 } 150 151 public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri, byte[] payload, String resourceFormat, List<Header> headers, int timeoutLoading) { 152 HttpPut httpPut = new HttpPut(resourceUri); 153 return issueResourceRequest(resourceFormat, httpPut, payload, headers, timeoutLoading); 154 } 155 156 public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri, byte[] payload, String resourceFormat, int timeoutLoading) { 157 HttpPut httpPut = new HttpPut(resourceUri); 158 return issueResourceRequest(resourceFormat, httpPut, payload, null, timeoutLoading); 159 } 160 161 public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri, byte[] payload, String resourceFormat, List<Header> headers, int timeoutLoading) { 162 HttpPost httpPost = new HttpPost(resourceUri); 163 return issueResourceRequest(resourceFormat, httpPost, payload, headers, timeoutLoading); 164 } 165 166 167 public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri, byte[] payload, String resourceFormat, int timeoutLoading) { 168 return issuePostRequest(resourceUri, payload, resourceFormat, null, timeoutLoading); 169 } 170 171 public Bundle issueGetFeedRequest(URI resourceUri, String resourceFormat) { 172 HttpGet httpget = new HttpGet(resourceUri); 173 configureFhirRequest(httpget, resourceFormat); 174 HttpResponse response = sendRequest(httpget); 175 return unmarshalReference(response, resourceFormat); 176 } 177 178 private void setAuth(HttpRequest httpget) { 179 if (password != null) { 180 try { 181 byte[] b = Base64.encodeBase64((username+":"+password).getBytes("ASCII")); 182 String b64 = new String(b, StandardCharsets.US_ASCII); 183 httpget.setHeader("Authorization", "Basic " + b64); 184 } catch (UnsupportedEncodingException e) { 185 } 186 } 187 } 188 189 public Bundle postBatchRequest(URI resourceUri, byte[] payload, String resourceFormat, int timeoutLoading) { 190 HttpPost httpPost = new HttpPost(resourceUri); 191 configureFhirRequest(httpPost, resourceFormat); 192 HttpResponse response = sendPayload(httpPost, payload, proxy, timeoutLoading); 193 return unmarshalFeed(response, resourceFormat); 194 } 195 196 public boolean issueDeleteRequest(URI resourceUri) { 197 HttpDelete deleteRequest = new HttpDelete(resourceUri); 198 HttpResponse response = sendRequest(deleteRequest); 199 int responseStatusCode = response.getStatusLine().getStatusCode(); 200 boolean deletionSuccessful = false; 201 if(responseStatusCode == 204) { 202 deletionSuccessful = true; 203 } 204 return deletionSuccessful; 205 } 206 207 /*********************************************************** 208 * Request/Response Helper methods 209 ***********************************************************/ 210 211 protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, int timeoutLoading) { 212 return issueResourceRequest(resourceFormat, request, null, timeoutLoading); 213 } 214 215 /** 216 * @param resourceFormat 217 * @return 218 */ 219 protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, byte[] payload, int timeoutLoading) { 220 return issueResourceRequest(resourceFormat, request, payload, null, timeoutLoading); 221 } 222 223 /** 224 * @param resourceFormat 225 * @return 226 */ 227 protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, byte[] payload, List<Header> headers, int timeoutLoading) { 228 configureFhirRequest(request, resourceFormat, headers); 229 HttpResponse response = null; 230 if(request instanceof HttpEntityEnclosingRequest && payload != null) { 231 response = sendPayload((HttpEntityEnclosingRequestBase)request, payload, proxy, timeoutLoading); 232 } else if (request instanceof HttpEntityEnclosingRequest && payload == null){ 233 throw new EFhirClientException("PUT and POST requests require a non-null payload"); 234 } else { 235 response = sendRequest(request); 236 } 237 T resource = unmarshalReference(response, resourceFormat); 238 return new ResourceRequest<T>(resource, response.getStatusLine().getStatusCode(), getLocationHeader(response)); 239 } 240 241 242 /** 243 * Method adds required request headers. 244 * TODO handle JSON request as well. 245 * 246 * @param request 247 */ 248 protected void configureFhirRequest(HttpRequest request, String format) { 249 configureFhirRequest(request, format, null); 250 } 251 252 /** 253 * Method adds required request headers. 254 * TODO handle JSON request as well. 255 * 256 * @param request 257 */ 258 protected void configureFhirRequest(HttpRequest request, String format, List<Header> headers) { 259 request.addHeader("User-Agent", "Java FHIR Client for FHIR"); 260 261 if (format != null) { 262 request.addHeader("Accept",format); 263 request.addHeader("Content-Type", format + ";charset=" + DEFAULT_CHARSET); 264 } 265 request.addHeader("Accept-Charset", DEFAULT_CHARSET); 266 if(headers != null) { 267 for(Header header : headers) { 268 request.addHeader(header); 269 } 270 } 271 setAuth(request); 272 } 273 274 /** 275 * Method posts request payload 276 * 277 * @param request 278 * @param payload 279 * @return 280 */ 281 @SuppressWarnings({ "resource", "deprecation" }) 282 protected HttpResponse sendPayload(HttpEntityEnclosingRequestBase request, byte[] payload, HttpHost proxy, int timeoutLoading) { 283 HttpResponse response = null; 284 boolean ok = false; 285 long t = System.currentTimeMillis(); 286 int tryCount = 0; 287 while (!ok) { 288 try { 289 tryCount++; 290 HttpClient httpclient = new DefaultHttpClient(); 291 HttpParams params = httpclient.getParams(); 292 HttpConnectionParams.setConnectionTimeout(params, timeout); 293 HttpConnectionParams.setSoTimeout(params, timeout * timeoutLoading); 294 295 if(proxy != null) { 296 httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 297 } 298 request.setEntity(new ByteArrayEntity(payload)); 299 log(request); 300 response = httpclient.execute(request); 301 ok = true; 302 } catch(IOException ioe) { 303 System.out.println(ioe.getMessage()+" ("+(System.currentTimeMillis()-t)+"ms / "+Utilities.describeSize(payload.length)+")"); 304 if (tryCount <= retryCount || (tryCount < 3 && ioe instanceof org.apache.http.conn.ConnectTimeoutException)) { 305 ok = false; 306 } else { 307 throw new EFhirClientException("Error sending HTTP Post/Put Payload: "+ioe.getMessage(), ioe); 308 } 309 } 310 } 311 return response; 312 } 313 314 /** 315 * @param request 316 * @return 317 */ 318 protected HttpResponse sendRequest(HttpUriRequest request) { 319 HttpResponse response = null; 320 try { 321 HttpClient httpclient = new DefaultHttpClient(); 322 log(request); 323 HttpParams params = httpclient.getParams(); 324 HttpConnectionParams.setConnectionTimeout(params, timeout); 325 HttpConnectionParams.setSoTimeout(params, timeout); 326 if(proxy != null) { 327 httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 328 } 329 response = httpclient.execute(request); 330 } catch(IOException ioe) { 331 if (ClientUtils.debugging ) { 332 ioe.printStackTrace(); 333 } 334 throw new EFhirClientException("Error sending Http Request: "+ioe.getMessage(), ioe); 335 } 336 return response; 337 } 338 339 /** 340 * Unmarshals a resource from the response stream. 341 * 342 * @param response 343 * @return 344 */ 345 @SuppressWarnings("unchecked") 346 protected <T extends Resource> T unmarshalReference(HttpResponse response, String format) { 347 T resource = null; 348 OperationOutcome error = null; 349 byte[] cnt = log(response); 350 if (cnt != null) { 351 try { 352 resource = (T)getParser(format).parse(cnt); 353 if (resource instanceof OperationOutcome && hasError((OperationOutcome)resource)) { 354 error = (OperationOutcome) resource; 355 } 356 } catch(IOException ioe) { 357 throw new EFhirClientException("Error reading Http Response: "+ioe.getMessage(), ioe); 358 } catch(Exception e) { 359 throw new EFhirClientException("Error parsing response message: "+e.getMessage(), e); 360 } 361 } 362 if(error != null) { 363 throw new EFhirClientException("Error from server: "+ResourceUtilities.getErrorDescription(error), error); 364 } 365 return resource; 366 } 367 368 /** 369 * Unmarshals Bundle from response stream. 370 * 371 * @param response 372 * @return 373 */ 374 protected Bundle unmarshalFeed(HttpResponse response, String format) { 375 Bundle feed = null; 376 byte[] cnt = log(response); 377 String contentType = response.getHeaders("Content-Type")[0].getValue(); 378 OperationOutcome error = null; 379 try { 380 if (cnt != null) { 381 if(contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains("text/xml+fhir")) { 382 Resource rf = getParser(format).parse(cnt); 383 if (rf instanceof Bundle) 384 feed = (Bundle) rf; 385 else if (rf instanceof OperationOutcome && hasError((OperationOutcome) rf)) { 386 error = (OperationOutcome) rf; 387 } else { 388 throw new EFhirClientException("Error reading server response: a resource was returned instead"); 389 } 390 } 391 } 392 } catch(IOException ioe) { 393 throw new EFhirClientException("Error reading Http Response", ioe); 394 } catch(Exception e) { 395 throw new EFhirClientException("Error parsing response message", e); 396 } 397 if(error != null) { 398 throw new EFhirClientException("Error from server: "+ResourceUtilities.getErrorDescription(error), error); 399 } 400 return feed; 401 } 402 403 private boolean hasError(OperationOutcome oo) { 404 for (OperationOutcomeIssueComponent t : oo.getIssue()) 405 if (t.getSeverity() == IssueSeverity.ERROR || t.getSeverity() == IssueSeverity.FATAL) 406 return true; 407 return false; 408 } 409 410 protected String getLocationHeader(HttpResponse response) { 411 String location = null; 412 if(response.getHeaders("location").length > 0) {//TODO Distinguish between both cases if necessary 413 location = response.getHeaders("location")[0].getValue(); 414 } else if(response.getHeaders("content-location").length > 0) { 415 location = response.getHeaders("content-location")[0].getValue(); 416 } 417 return location; 418 } 419 420 421 /***************************************************************** 422 * Client connection methods 423 * ***************************************************************/ 424 425 public HttpURLConnection buildConnection(URI baseServiceUri, String tail) { 426 try { 427 HttpURLConnection client = (HttpURLConnection) baseServiceUri.resolve(tail).toURL().openConnection(); 428 return client; 429 } catch(MalformedURLException mue) { 430 throw new EFhirClientException("Invalid Service URL", mue); 431 } catch(IOException ioe) { 432 throw new EFhirClientException("Unable to establish connection to server: " + baseServiceUri.toString() + tail, ioe); 433 } 434 } 435 436 public HttpURLConnection buildConnection(URI baseServiceUri, ResourceType resourceType, String id) { 437 return buildConnection(baseServiceUri, ResourceAddress.buildRelativePathFromResourceType(resourceType, id)); 438 } 439 440 /****************************************************************** 441 * Other general helper methods 442 * ****************************************************************/ 443 444 445 public <T extends Resource> byte[] getResourceAsByteArray(T resource, boolean pretty, boolean isJson) { 446 ByteArrayOutputStream baos = null; 447 byte[] byteArray = null; 448 try { 449 baos = new ByteArrayOutputStream(); 450 IParser parser = null; 451 if(isJson) { 452 parser = new JsonParser(); 453 } else { 454 parser = new XmlParser(); 455 } 456 parser.setOutputStyle(pretty ? OutputStyle.PRETTY : OutputStyle.NORMAL); 457 parser.compose(baos, resource); 458 baos.close(); 459 byteArray = baos.toByteArray(); 460 baos.close(); 461 } catch (Exception e) { 462 try{ 463 baos.close(); 464 }catch(Exception ex) { 465 throw new EFhirClientException("Error closing output stream", ex); 466 } 467 throw new EFhirClientException("Error converting output stream to byte array", e); 468 } 469 return byteArray; 470 } 471 472 public byte[] getFeedAsByteArray(Bundle feed, boolean pretty, boolean isJson) { 473 ByteArrayOutputStream baos = null; 474 byte[] byteArray = null; 475 try { 476 baos = new ByteArrayOutputStream(); 477 IParser parser = null; 478 if(isJson) { 479 parser = new JsonParser(); 480 } else { 481 parser = new XmlParser(); 482 } 483 parser.setOutputStyle(pretty ? OutputStyle.PRETTY : OutputStyle.NORMAL); 484 parser.compose(baos, feed); 485 baos.close(); 486 byteArray = baos.toByteArray(); 487 baos.close(); 488 } catch (Exception e) { 489 try{ 490 baos.close(); 491 }catch(Exception ex) { 492 throw new EFhirClientException("Error closing output stream", ex); 493 } 494 throw new EFhirClientException("Error converting output stream to byte array", e); 495 } 496 return byteArray; 497 } 498 499 public Calendar getLastModifiedResponseHeaderAsCalendarObject(URLConnection serverConnection) { 500 String dateTime = null; 501 try { 502 dateTime = serverConnection.getHeaderField("Last-Modified"); 503 SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", new Locale("en", "US")); 504 Date lastModifiedTimestamp = format.parse(dateTime); 505 Calendar calendar=Calendar.getInstance(); 506 calendar.setTime(lastModifiedTimestamp); 507 return calendar; 508 } catch(ParseException pe) { 509 throw new EFhirClientException("Error parsing Last-Modified response header " + dateTime, pe); 510 } 511 } 512 513 protected IParser getParser(String format) { 514 if(StringUtils.isBlank(format)) { 515 format = ResourceFormat.RESOURCE_XML.getHeader(); 516 } 517 if(format.equalsIgnoreCase("json") || format.equalsIgnoreCase(ResourceFormat.RESOURCE_JSON.getHeader()) || format.equalsIgnoreCase(ResourceFormat.RESOURCE_JSON.getHeader())) { 518 return new JsonParser(); 519 } else if(format.equalsIgnoreCase("xml") || format.equalsIgnoreCase(ResourceFormat.RESOURCE_XML.getHeader()) || format.equalsIgnoreCase(ResourceFormat.RESOURCE_XML.getHeader())) { 520 return new XmlParser(); 521 } else { 522 throw new EFhirClientException("Invalid format: " + format); 523 } 524 } 525 526 public Bundle issuePostFeedRequest(URI resourceUri, Map<String, String> parameters, String resourceName, Resource resource, String resourceFormat) throws IOException { 527 HttpPost httppost = new HttpPost(resourceUri); 528 String boundary = "----WebKitFormBoundarykbMUo6H8QaUnYtRy"; 529 httppost.addHeader("Content-Type", "multipart/form-data; boundary="+boundary); 530 httppost.addHeader("Accept", resourceFormat); 531 configureFhirRequest(httppost, null); 532 HttpResponse response = sendPayload(httppost, encodeFormSubmission(parameters, resourceName, resource, boundary)); 533 return unmarshalFeed(response, resourceFormat); 534 } 535 536 private byte[] encodeFormSubmission(Map<String, String> parameters, String resourceName, Resource resource, String boundary) throws IOException { 537 ByteArrayOutputStream b = new ByteArrayOutputStream(); 538 OutputStreamWriter w = new OutputStreamWriter(b, "UTF-8"); 539 for (String name : parameters.keySet()) { 540 w.write("--"); 541 w.write(boundary); 542 w.write("\r\nContent-Disposition: form-data; name=\""+name+"\"\r\n\r\n"); 543 w.write(parameters.get(name)+"\r\n"); 544 } 545 w.write("--"); 546 w.write(boundary); 547 w.write("\r\nContent-Disposition: form-data; name=\""+resourceName+"\"\r\n\r\n"); 548 w.close(); 549 JsonParser json = new JsonParser(); 550 json.setOutputStyle(OutputStyle.NORMAL); 551 json.compose(b, resource); 552 b.close(); 553 w = new OutputStreamWriter(b, "UTF-8"); 554 w.write("\r\n--"); 555 w.write(boundary); 556 w.write("--"); 557 w.close(); 558 return b.toByteArray(); 559 } 560 561 /** 562 * Method posts request payload 563 * 564 * @param request 565 * @param payload 566 * @return 567 */ 568 protected HttpResponse sendPayload(HttpEntityEnclosingRequestBase request, byte[] payload) { 569 HttpResponse response = null; 570 try { 571 log(request); 572 HttpClient httpclient = new DefaultHttpClient(); 573 request.setEntity(new ByteArrayEntity(payload)); 574 response = httpclient.execute(request); 575 log(response); 576 } catch(IOException ioe) { 577 throw new EFhirClientException("Error sending HTTP Post/Put Payload: "+ioe.getMessage(), ioe); 578 } 579 return response; 580 } 581 582 private void log(HttpUriRequest request) { 583 if (logger != null) { 584 List<String> headers = new ArrayList<>(); 585 for (Header h : request.getAllHeaders()) { 586 headers.add(h.toString()); 587 } 588 logger.logRequest(request.getMethod(), request.getURI().toString(), headers, null); 589 } 590 } 591 private void log(HttpEntityEnclosingRequestBase request) { 592 if (logger != null) { 593 List<String> headers = new ArrayList<>(); 594 for (Header h : request.getAllHeaders()) { 595 headers.add(h.toString()); 596 } 597 byte[] cnt = null; 598 InputStream s; 599 try { 600 s = request.getEntity().getContent(); 601 cnt = IOUtils.toByteArray(s); 602 s.close(); 603 } catch (Exception e) { 604 } 605 logger.logRequest(request.getMethod(), request.getURI().toString(), headers, cnt); 606 } 607 } 608 609 private byte[] log(HttpResponse response) { 610 byte[] cnt = null; 611 try { 612 InputStream s = response.getEntity().getContent(); 613 cnt = IOUtils.toByteArray(s); 614 s.close(); 615 } catch (Exception e) { 616 } 617 if (logger != null) { 618 List<String> headers = new ArrayList<>(); 619 for (Header h : response.getAllHeaders()) { 620 headers.add(h.toString()); 621 } 622 logger.logResponse(response.getStatusLine().toString(), headers, cnt); 623 } 624 return cnt; 625 } 626 627 public ToolingClientLogger getLogger() { 628 return logger; 629 } 630 631 public void setLogger(ToolingClientLogger logger) { 632 this.logger = logger; 633 } 634 635 636 /** 637 * Used for debugging 638 * 639 * @param instream 640 * @return 641 */ 642 protected String writeInputStreamAsString(InputStream instream) { 643 String value = null; 644 try { 645 value = IOUtils.toString(instream, "UTF-8"); 646 System.out.println(value); 647 648 } catch(IOException ioe) { 649 //Do nothing 650 } 651 return value; 652 } 653 654 public int getRetryCount() { 655 return retryCount; 656 } 657 658 public void setRetryCount(int retryCount) { 659 this.retryCount = retryCount; 660 } 661 662 663}