• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

smartsheet / smartsheet-java-sdk / #57

29 Jul 2025 04:55PM UTC coverage: 60.803% (+0.2%) from 60.611%
#57

push

github

web-flow
Added new sharing endpoints. Deprecated old sharing endpoints. (#125)

* Added new sharing endpoints.
Deprecated old endpoints.

* Updated to 3.3.0 for deprecation date.

* Removed amp.

* fixed `&`

106 of 157 new or added lines in 9 files covered. (67.52%)

4269 of 7021 relevant lines covered (60.8%)

0.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.5
/src/main/java/com/smartsheet/api/internal/http/DefaultHttpClient.java
1
/*
2
 * Copyright (C) 2025 Smartsheet
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.smartsheet.api.internal.http;
18

19
import com.smartsheet.api.Trace;
20
import com.smartsheet.api.internal.json.JacksonJsonSerializer;
21
import com.smartsheet.api.internal.json.JsonSerializer;
22
import com.smartsheet.api.internal.util.StreamUtil;
23
import com.smartsheet.api.internal.util.Util;
24
import com.smartsheet.api.models.Error;
25
import org.apache.http.Header;
26
import org.apache.http.NoHttpResponseException;
27
import org.apache.http.client.ClientProtocolException;
28
import org.apache.http.client.NonRepeatableRequestException;
29
import org.apache.http.client.config.RequestConfig;
30
import org.apache.http.client.methods.CloseableHttpResponse;
31
import org.apache.http.client.methods.HttpDelete;
32
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
33
import org.apache.http.client.methods.HttpGet;
34
import org.apache.http.client.methods.HttpPost;
35
import org.apache.http.client.methods.HttpPut;
36
import org.apache.http.client.methods.HttpRequestBase;
37
import org.apache.http.client.methods.HttpRequestWrapper;
38
import org.apache.http.entity.ContentType;
39
import org.apache.http.entity.InputStreamEntity;
40
import org.apache.http.impl.client.CloseableHttpClient;
41
import org.apache.http.impl.client.HttpClients;
42
import org.apache.http.protocol.BasicHttpContext;
43
import org.apache.http.protocol.HttpContext;
44
import org.jetbrains.annotations.Nullable;
45
import org.slf4j.Logger;
46
import org.slf4j.LoggerFactory;
47

48
import java.io.ByteArrayInputStream;
49
import java.io.IOException;
50
import java.io.InputStream;
51
import java.io.OutputStream;
52
import java.io.PrintWriter;
53
import java.util.HashMap;
54
import java.util.HashSet;
55
import java.util.Map;
56
import java.util.Random;
57
import java.util.Set;
58

59
/**
60
 * This is the Apache HttpClient based HttpClient implementation.
61
 * <p>
62
 * Thread Safety: This class is thread safe because it is immutable and the underlying Apache CloseableHttpClient is
63
 * thread safe.
64
 *
65
 * @see <a href="http://hc.apache.org/httpcomponents-client-ga/index.html">Apache HttpClient</a>
66
 */
67
public class DefaultHttpClient implements HttpClient {
68

69
    /**
70
     * logger for general errors, warnings, etc
71
     */
72
    protected static final Logger logger = LoggerFactory.getLogger(DefaultHttpClient.class);
1✔
73

74
    /**
75
     * used by default retry/timeout logic and available for overriders
76
     */
77
    protected static final String JSON_MIME_TYPE = ContentType.APPLICATION_JSON.getMimeType();
1✔
78

79
    protected JsonSerializer jsonSerializer;
80

81
    protected long maxRetryTimeMillis = 15000;
1✔
82

83
    /**
84
     * Represents the underlying Apache CloseableHttpClient.
85
     * <p>
86
     * It will be initialized in constructor and will not change afterwards.
87
     */
88
    private final CloseableHttpClient httpClient;
89

90
    /**
91
     * The apache http response.
92
     */
93
    private CloseableHttpResponse apacheHttpResponse;
94

95
    /**
96
     * to avoid creating new sets for each call (we use Sets for practical and perf reasons)
97
     */
98
    private static final Set<Trace> REQUEST_RESPONSE_SUMMARY = Set.of(
1✔
99
            Trace.RequestHeaders,
100
            Trace.RequestBodySummary,
101
            Trace.ResponseHeaders,
102
            Trace.ResponseBodySummary
103
    );
104

105
    /**
106
     * default values for trace-logging extracted from system-properties (can still be overwritten at the instance level)
107
     */
108
    private static final boolean TRACE_PRETTY_PRINT_DEFAULT = Boolean.parseBoolean(System.getProperty("Smartsheet.trace.pretty", "true"));
1✔
109

110
    // empty by default
111
    private static final Set<Trace> TRACE_DEFAULT_TRACE_SET = Trace.parse(System.getProperty("Smartsheet.trace.parts"));
1✔
112

113
    /**
114
     * where to send trace logs
115
     */
116
    private static PrintWriter traceWriter;
117

118
    private final Random random = new Random();
1✔
119

120
    static {
121
        // default trace stream
122
        setTraceStream(System.out);
1✔
123
        if (!TRACE_DEFAULT_TRACE_SET.isEmpty()) {
1✔
124
            traceWriter.println("default trace logging - pretty:" + TRACE_PRETTY_PRINT_DEFAULT + " parts:" + TRACE_DEFAULT_TRACE_SET);
×
125
        }
126
    }
1✔
127

128
    /**
129
     * the set of Trace levels to use in trace-logging
130
     */
131
    private final Set<Trace> traces = new HashSet<>(TRACE_DEFAULT_TRACE_SET);
1✔
132

133
    /**
134
     * whether to log pretty or compact
135
     */
136
    private boolean tracePrettyPrint = TRACE_PRETTY_PRINT_DEFAULT;
1✔
137

138
    private static final String LOG_ARG = "{}";
139
    private static final String ERROR_OCCURRED = "Error occurred.";
140

141
    /**
142
     * Constructor.
143
     */
144
    public DefaultHttpClient() {
145
        this(HttpClients.createDefault(), new JacksonJsonSerializer());
1✔
146
    }
1✔
147

148
    /**
149
     * Constructor.
150
     * <p>
151
     * Parameters: - httpClient : the Apache CloseableHttpClient to use
152
     * <p>
153
     * Exceptions: - IllegalArgumentException : if any argument is null
154
     *
155
     * @param httpClient the http client
156
     */
157
    public DefaultHttpClient(CloseableHttpClient httpClient, JsonSerializer jsonSerializer) {
1✔
158
        this.httpClient = Util.throwIfNull(httpClient);
1✔
159
        this.jsonSerializer = jsonSerializer;
1✔
160
    }
1✔
161

162
    /**
163
     * Log to the SLF4J logger (level based upon response status code). Override this function to add logging
164
     * or capture performance metrics.
165
     *
166
     * @param request        request
167
     * @param requestEntity  request body
168
     * @param response       response
169
     * @param responseEntity response body
170
     * @param durationMillis response time in ms
171
     */
172
    public void logRequest(HttpRequestBase request, HttpEntitySnapshot requestEntity,
173
                           HttpResponse response, HttpEntitySnapshot responseEntity, long durationMillis) throws IOException {
174

175
        logger.info("{} {}, Response Code:{}, Request completed in {} ms", request.getMethod(), request.getURI(),
1✔
176
                response.getStatusCode(), durationMillis);
1✔
177
        logger.debug(LOG_ARG, RequestAndResponseData.of(request, requestEntity, response, responseEntity, REQUEST_RESPONSE_SUMMARY));
1✔
178
    }
1✔
179

180
    /**
181
     * Make an HTTP request and return the response.
182
     *
183
     * @param smartsheetRequest the smartsheet request
184
     * @return the HTTP response
185
     * @throws HttpClientException the HTTP client exception
186
     */
187
    public HttpResponse request(HttpRequest smartsheetRequest) throws HttpClientException {
188
        Util.throwIfNull(smartsheetRequest);
1✔
189
        if (smartsheetRequest.getUri() == null) {
1✔
190
            throw new IllegalArgumentException("A Request URI is required.");
1✔
191
        }
192

193
        long start = System.currentTimeMillis();
1✔
194

195
        HttpRequestBase apacheHttpRequest;
196
        HttpResponse smartsheetResponse;
197

198
        InputStream bodyStream = null;
1✔
199
        if (smartsheetRequest.getEntity() != null && smartsheetRequest.getEntity().getContent() != null) {
1✔
200
            bodyStream = smartsheetRequest.getEntity().getContent();
1✔
201
        }
202
        // the retry logic will consume the body stream so we make sure it supports mark/reset and mark it
203
        boolean canRetryRequest = bodyStream == null || bodyStream.markSupported();
1✔
204
        if (!canRetryRequest) {
1✔
205
            try {
206
                // attempt to wrap the body stream in a input-stream that does support mark/reset
207
                bodyStream = new ByteArrayInputStream(StreamUtil.readBytesFromStream(bodyStream));
1✔
208
                // close the old stream (just to be tidy) and then replace it with a reset-able stream
209
                smartsheetRequest.getEntity().getContent().close();
1✔
210
                smartsheetRequest.getEntity().setContent(bodyStream);
1✔
211
                canRetryRequest = true;
1✔
212
            } catch (IOException ignore) {
1✔
213
            }
1✔
214
        }
215

216
        // the retry loop
217
        while (true) {
218
            int attempt = 0;
1✔
219
            apacheHttpRequest = createApacheRequest(smartsheetRequest);
1✔
220

221
            // Set HTTP headers
222
            if (smartsheetRequest.getHeaders() != null) {
1✔
223
                for (Map.Entry<String, String> header : smartsheetRequest.getHeaders().entrySet()) {
1✔
224
                    apacheHttpRequest.addHeader(header.getKey(), header.getValue());
1✔
225
                }
1✔
226
            }
227

228
            HttpEntitySnapshot responseEntityCopy = null;
1✔
229
            // Set HTTP entity
230
            HttpEntitySnapshot requestEntityCopy = copyRequestEntity(smartsheetRequest, apacheHttpRequest);
1✔
231

232
            // mark the body so we can reset on retry
233
            if (canRetryRequest && bodyStream != null) {
1✔
234
                bodyStream.mark((int) smartsheetRequest.getEntity().getContentLength());
1✔
235
            }
236

237
            // Make the HTTP request
238
            smartsheetResponse = new HttpResponse();
1✔
239
            HttpContext context = new BasicHttpContext();
1✔
240
            try {
241
                long startTime = System.currentTimeMillis();
1✔
242
                apacheHttpResponse = this.httpClient.execute(apacheHttpRequest, context);
1✔
243
                long endTime = System.currentTimeMillis();
1✔
244

245
                HttpEntitySnapshot newResponseCopy = updateWithResponse(apacheHttpRequest, context, smartsheetResponse);
1✔
246
                if (newResponseCopy != null) {
1✔
247
                    responseEntityCopy = newResponseCopy;
1✔
248
                }
249

250
                long responseTime = endTime - startTime;
1✔
251
                logRequest(apacheHttpRequest, requestEntityCopy, smartsheetResponse, responseEntityCopy, responseTime);
1✔
252

253
                // trace-logging of request and response (if so configured)
254
                if (traces.size() > 0) {
1✔
255
                    RequestAndResponseData requestAndResponseData = RequestAndResponseData.of(apacheHttpRequest,
1✔
256
                            requestEntityCopy, smartsheetResponse, responseEntityCopy, traces);
257
                    traceWriter.println(requestAndResponseData.toString(tracePrettyPrint));
1✔
258
                }
259

260
                if (smartsheetResponse.getStatusCode() == 200) {
1✔
261
                    // call successful, exit the retry loop
262
                    break;
1✔
263
                }
264

265
                // the retry logic might consume the content stream so we make sure it supports mark/reset and mark it
266
                InputStream contentStream = smartsheetResponse.getEntity().getContent();
1✔
267
                if (!contentStream.markSupported()) {
1✔
268
                    // wrap the response stream in a input-stream that does support mark/reset
269
                    contentStream = new ByteArrayInputStream(StreamUtil.readBytesFromStream(contentStream));
1✔
270
                    // close the old stream (just to be tidy) and then replace it with a reset-able stream
271
                    smartsheetResponse.getEntity().getContent().close();
1✔
272
                    smartsheetResponse.getEntity().setContent(contentStream);
1✔
273
                }
274
                try {
275
                    contentStream.mark((int) smartsheetResponse.getEntity().getContentLength());
1✔
276
                    long timeSpent = System.currentTimeMillis() - start;
1✔
277
                    if (!shouldRetry(++attempt, timeSpent, smartsheetResponse)) {
1✔
278
                        // should not retry, or retry time exceeded, exit the retry loop
279
                        break;
280
                    }
281
                } finally {
282
                    if (bodyStream != null) {
1✔
283
                        bodyStream.reset();
1✔
284
                    }
285
                    contentStream.reset();
1✔
286
                }
287
                // moving this to finally causes issues because socket is closed (which means response stream is closed)
288
                this.releaseConnection();
×
289

290
            } catch (ClientProtocolException e) {
1✔
291
                logger.warn("ClientProtocolException " + e.getMessage());
1✔
292
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
1✔
293
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
294
                try {
295
                    // if this is a PUT and was retried by the http client, the body content stream is at the
296
                    // end and is a NonRepeatableRequest. If we marked the body content stream prior to execute,
297
                    // reset and retry
298
                    if (canRetryRequest && e.getCause() instanceof NonRepeatableRequestException) {
1✔
299
                        if (smartsheetRequest.getEntity() != null) {
×
300
                            smartsheetRequest.getEntity().getContent().reset();
×
301
                        }
302
                        continue;
×
303
                    }
304
                } catch (IOException ignore) {
×
305
                }
1✔
306
                throw new HttpClientException(ERROR_OCCURRED, e);
1✔
307
            } catch (NoHttpResponseException e) {
×
308
                logger.warn("NoHttpResponseException {}", e.getMessage());
×
309
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
×
310
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
311
                try {
312
                    // check to see if the response was empty and this was a POST or PATCH. All other HTTP methods
313
                    // will be automatically retried by the http client.
314
                    // (POST and PATCH are non-idempotent and are not retried automatically, but are safe for us to retry)
NEW
315
                    if (canRetryRequest &&
×
NEW
316
                            (smartsheetRequest.getMethod() == HttpMethod.POST ||
×
NEW
317
                             smartsheetRequest.getMethod() == HttpMethod.PATCH)) {
×
318
                        if (smartsheetRequest.getEntity() != null) {
×
319
                            smartsheetRequest.getEntity().getContent().reset();
×
320
                        }
321
                        continue;
×
322
                    }
323
                } catch (IOException ignore) {
×
324
                }
×
325
                throw new HttpClientException(ERROR_OCCURRED, e);
×
326
            } catch (IOException e) {
1✔
327
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
1✔
328
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
329
                throw new HttpClientException(ERROR_OCCURRED, e);
1✔
330
            }
×
331
        }
×
332
        return smartsheetResponse;
1✔
333
    }
334

335
    private HttpEntitySnapshot copyRequestEntity(HttpRequest smartsheetRequest, HttpRequestBase apacheHttpRequest) {
336
        final HttpEntity entity = smartsheetRequest.getEntity();
1✔
337
        HttpEntitySnapshot requestEntityCopy = null;
1✔
338
        if (apacheHttpRequest instanceof HttpEntityEnclosingRequestBase && entity != null && entity.getContent() != null) {
1✔
339
            try {
340
                // we need access to the original request stream so we can log it (in the event of errors and/or tracing)
341
                requestEntityCopy = new HttpEntitySnapshot(entity);
1✔
342
            } catch (IOException iox) {
×
343
                logger.error("failed to make copy of original request entity", iox);
×
344
            }
1✔
345

346
            InputStreamEntity streamEntity = new InputStreamEntity(entity.getContent(), entity.getContentLength());
1✔
347
            // why?  not supported by library?
348
            streamEntity.setChunked(false);
1✔
349
            ((HttpEntityEnclosingRequestBase) apacheHttpRequest).setEntity(streamEntity);
1✔
350
        }
351
        return requestEntityCopy;
1✔
352
    }
353

354
    @Nullable
355
    private HttpEntitySnapshot updateWithResponse(HttpRequestBase apacheHttpRequest, HttpContext context,
356
                                                  HttpResponse smartsheetResponse) throws IOException {
357
        // Set request headers to values ACTUALLY SENT (not just created by us), this would include:
358
        // 'Connection', 'Accept-Encoding', etc. However, if a proxy is used, this may be the proxy's CONNECT
359
        // request, hence the test for HTTP method first
360
        Object httpRequest = context.getAttribute("http.request");
1✔
361
        if (httpRequest != null && HttpRequestWrapper.class.isAssignableFrom(httpRequest.getClass())) {
1✔
362
            HttpRequestWrapper actualRequest = (HttpRequestWrapper) httpRequest;
1✔
363
            switch (HttpMethod.valueOf(actualRequest.getMethod())) {
1✔
364
                case GET:
365
                case POST:
366
                case PUT:
367
                case DELETE:
368
                case PATCH:
369
                    apacheHttpRequest.setHeaders(actualRequest.getAllHeaders());
1✔
370
                    break;
1✔
371
                default:
372
                    break;
373
            }
374
        }
375

376
        // Set returned headers
377
        smartsheetResponse.setHeaders(new HashMap<>());
1✔
378
        for (Header header : apacheHttpResponse.getAllHeaders()) {
1✔
379
            smartsheetResponse.getHeaders().put(header.getName(), header.getValue());
1✔
380
        }
381
        smartsheetResponse.setStatus(apacheHttpResponse.getStatusLine().getStatusCode(),
1✔
382
                apacheHttpResponse.getStatusLine().toString());
1✔
383

384
        // Set returned entities
385
        if (apacheHttpResponse.getEntity() != null) {
1✔
386
            HttpEntity httpEntity = new HttpEntity();
1✔
387
            httpEntity.setContentType(apacheHttpResponse.getEntity().getContentType().getValue());
1✔
388
            httpEntity.setContentLength(apacheHttpResponse.getEntity().getContentLength());
1✔
389
            httpEntity.setContent(apacheHttpResponse.getEntity().getContent());
1✔
390
            smartsheetResponse.setEntity(httpEntity);
1✔
391
            return new HttpEntitySnapshot(httpEntity);
1✔
392
        }
393

394
        return null;
×
395
    }
396

397
    /**
398
     * Create the Apache HTTP request. Override this function to inject additional
399
     * haaders in the request or use a proxy.
400
     *
401
     * @param smartsheetRequest (request method and base URI come from here)
402
     * @return the Apache HTTP request
403
     */
404
    public HttpRequestBase createApacheRequest(HttpRequest smartsheetRequest) {
405
        HttpRequestBase apacheHttpRequest;
406

407
        // Create Apache HTTP request based on the smartsheetRequest request type
408
        switch (smartsheetRequest.getMethod()) {
1✔
409
            case GET:
410
                apacheHttpRequest = new HttpGet(smartsheetRequest.getUri());
1✔
411
                break;
1✔
412
            case POST:
413
                apacheHttpRequest = new HttpPost(smartsheetRequest.getUri());
1✔
414
                break;
1✔
415
            case PUT:
416
                apacheHttpRequest = new HttpPut(smartsheetRequest.getUri());
1✔
417
                break;
1✔
418
            case DELETE:
419
                apacheHttpRequest = new HttpDelete(smartsheetRequest.getUri());
1✔
420
                break;
1✔
421
            case PATCH:
422
                apacheHttpRequest = new HttpPatch(smartsheetRequest.getUri());
1✔
423
                break;
1✔
424
            default:
425
                throw new UnsupportedOperationException("Request method " + smartsheetRequest.getMethod() + " is not supported!");
×
426
        }
427

428
        RequestConfig.Builder builder = RequestConfig.custom();
1✔
429
        if (apacheHttpRequest.getConfig() != null) {
1✔
430
            builder = RequestConfig.copy(apacheHttpRequest.getConfig());
×
431
        }
432
        builder.setRedirectsEnabled(true);
1✔
433
        RequestConfig config = builder.build();
1✔
434
        apacheHttpRequest.setConfig(config);
1✔
435
        return apacheHttpRequest;
1✔
436
    }
437

438
    /**
439
     * Set the max retry time for API calls which fail and are retry-able.
440
     */
441
    public void setMaxRetryTimeMillis(long maxRetryTimeMillis) {
442
        this.maxRetryTimeMillis = maxRetryTimeMillis;
×
443
    }
×
444

445
    /**
446
     * The backoff calculation routine. Uses exponential backoff. If the maximum elapsed time
447
     * has expired, this calculation returns -1 causing the caller to fall out of the retry loop.
448
     *
449
     * @return -1 to fall out of retry loop, positive number indicates backoff time
450
     */
451
    public long calcBackoff(int previousAttempts, long totalElapsedTimeMillis, Error error) {
452

453
        long backoffMillis = (long) (Math.pow(2, previousAttempts) * 1000) + random.nextInt(1000);
×
454

455
        if (totalElapsedTimeMillis + backoffMillis > maxRetryTimeMillis) {
×
456
            logger.info(
×
457
                    "Elapsed time {} + backoff time {} exceeds max retry time {}, exiting retry loop",
458
                    totalElapsedTimeMillis,
×
459
                    backoffMillis,
×
460
                    maxRetryTimeMillis
×
461
            );
462
            return -1;
×
463
        }
464
        return backoffMillis;
×
465
    }
466

467
    /**
468
     * Called when an API request fails to determine if it can retry the request.
469
     * Calls calcBackoff to determine the time to wait in between retries.
470
     *
471
     * @param previousAttempts       number of attempts (including this one) to execute request
472
     * @param totalElapsedTimeMillis total time spent in millis for all previous (and this) attempt
473
     * @param response               the failed HttpResponse
474
     * @return true if this request can be retried
475
     */
476
    public boolean shouldRetry(int previousAttempts, long totalElapsedTimeMillis, HttpResponse response) {
477
        String contentType = response.getEntity().getContentType();
1✔
478
        if (contentType != null && !contentType.startsWith(JSON_MIME_TYPE)) {
1✔
479
            // it's not JSON; don't even try to parse it
480
            return false;
1✔
481
        }
482
        Error error;
483
        try {
484
            error = jsonSerializer.deserialize(Error.class, response.getEntity().getContent());
1✔
485
        } catch (IOException e) {
×
486
            return false;
×
487
        }
1✔
488
        switch (error.getErrorCode()) {
1✔
489
            case 4001:
490
                // Smartsheet.com is currently offline for system maintenance. Please check back again shortly.
491
            case 4002:
492
                // Server timeout exceeded. Request has failed
493
            case 4003:
494
                // Rate limit exceeded.
495
            case 4004:
496
                // An unexpected error has occurred. Please retry your request
497
                // If you encounter this error repeatedly, please contact api@smartsheet.com for assistance
498
                break;
×
499
            default:
500
                return false;
1✔
501
        }
502

503
        long backoffMillis = calcBackoff(previousAttempts, totalElapsedTimeMillis, error);
×
504
        if (backoffMillis < 0) {
×
505
            return false;
×
506
        }
507

508
        logger.info("HttpError StatusCode={}: Retrying in {} milliseconds", response.getStatusCode(), backoffMillis);
×
509
        try {
510
            Thread.sleep(backoffMillis);
×
511
        } catch (InterruptedException e) {
×
512
            logger.warn("sleep interrupted", e);
×
513
            return false;
×
514
        }
×
515
        return true;
×
516
    }
517

518
    /**
519
     * Close the HttpClient.
520
     *
521
     * @throws IOException Signals that an I/O exception has occurred.
522
     */
523
    @Override
524
    public void close() throws IOException {
525
        this.httpClient.close();
1✔
526
    }
1✔
527

528
    /* (non-Javadoc)
529
     * @see com.smartsheet.api.internal.http.HttpClient#releaseConnection()
530
     */
531
    @Override
532
    public void releaseConnection() {
533
        if (apacheHttpResponse != null) {
1✔
534
            try {
535
                apacheHttpResponse.close();
1✔
536
                apacheHttpResponse = null;
1✔
537
            } catch (IOException e) {
×
538
                logger.error("error closing Apache HttpResponse", e);
×
539
            }
1✔
540
        }
541
    }
1✔
542

543
    /**
544
     * set the traces for this client
545
     *
546
     * @param traces the fields to include in trace-logging
547
     */
548
    public void setTraces(Trace... traces) {
549
        this.traces.clear();
1✔
550
        for (Trace trace : traces) {
1✔
551
            if (!trace.addReplacements(this.traces)) {
1✔
552
                this.traces.add(trace);
×
553
            }
554
        }
555
    }
1✔
556

557
    /**
558
     * set whether to use nicely-formatted JSON or more compact format JSON in trace logging
559
     *
560
     * @param pretty whether to print JSON in a "pretty" format or compact
561
     */
562
    public void setTracePrettyPrint(boolean pretty) {
563
        tracePrettyPrint = pretty;
×
564
    }
×
565

566
    /**
567
     * only included for testing purposes
568
     */
569
    public static void setTraceStream(OutputStream traceStream) {
570
        traceWriter = new PrintWriter(traceStream, true);
1✔
571
    }
1✔
572
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc