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

smartsheet / smartsheet-java-sdk / #66

26 Jun 2026 09:41AM UTC coverage: 59.997% (-0.006%) from 60.003%
#66

push

github

web-flow
Prepare for release v4.1.0 (#180)

4510 of 7517 relevant lines covered (60.0%)

0.6 hits per line

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

69.13
/src/main/java/com/smartsheet/api/internal/SmartsheetImpl.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;
18

19
import com.smartsheet.api.AssetShareResources;
20
import com.smartsheet.api.ContactResources;
21
import com.smartsheet.api.EventResources;
22
import com.smartsheet.api.FavoriteResources;
23
import com.smartsheet.api.FolderResources;
24
import com.smartsheet.api.GroupResources;
25
import com.smartsheet.api.HomeResources;
26
import com.smartsheet.api.ImageUrlResources;
27
import com.smartsheet.api.PassthroughResources;
28
import com.smartsheet.api.ReportResources;
29
import com.smartsheet.api.SearchResources;
30
import com.smartsheet.api.ServerInfoResources;
31
import com.smartsheet.api.SheetResources;
32
import com.smartsheet.api.SightResources;
33
import com.smartsheet.api.Smartsheet;
34
import com.smartsheet.api.TokenResources;
35
import com.smartsheet.api.Trace;
36
import com.smartsheet.api.UserResources;
37
import com.smartsheet.api.WebhookResources;
38
import com.smartsheet.api.WorkspaceResources;
39
import com.smartsheet.api.internal.http.AndroidHttpClient;
40
import com.smartsheet.api.internal.http.DefaultHttpClient;
41
import com.smartsheet.api.internal.http.HttpClient;
42
import com.smartsheet.api.internal.json.JacksonJsonSerializer;
43
import com.smartsheet.api.internal.json.JsonSerializer;
44
import com.smartsheet.api.internal.util.CleanerUtil;
45
import com.smartsheet.api.internal.util.Util;
46
import org.apache.http.impl.client.HttpClients;
47

48
import java.io.IOException;
49
import java.net.URI;
50
import java.net.URL;
51
import java.util.Properties;
52
import java.util.concurrent.atomic.AtomicReference;
53

54
/**
55
 * This is the implementation of Smartsheet interface.
56
 * <p>
57
 * Thread Safety: This class is thread safe because all its mutable fields are safe-guarded using AtomicReference to
58
 * ensure atomic modifications, and also the underlying HttpClient and JsonSerializer interfaces are thread safe.
59
 */
60
public class SmartsheetImpl implements Smartsheet {
61

62
    /**
63
     * Represents the base URI of the Smartsheet REST API.
64
     * <p>
65
     * It will be initialized in constructor and will not change afterward.
66
     */
67
    private final URI baseURI;
68

69
    /**
70
     * Represents the AtomicReference for access token.
71
     * <p>
72
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
73
     * as null, and can be set via corresponding setter, therefore effectively the access token can be updated in the
74
     * SmartsheetImpl in thread safe manner.
75
     */
76
    private final AtomicReference<String> accessToken;
77

78
    /**
79
     * Represents the HttpClient.
80
     * <p>
81
     * It will be initialized in constructor and will not change afterward.
82
     */
83
    private final HttpClient httpClient;
84

85
    /**
86
     * Represents the JsonSerializer.
87
     * <p>
88
     * It will be initialized in constructor and will not change afterward.
89
     */
90
    private final JsonSerializer jsonSerializer;
91

92
    /**
93
     * Represents the AtomicReference for assumed user email.
94
     * <p>
95
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
96
     * as null, and can be set via corresponding setter, therefore effectively the assumed user can be updated in the
97
     * SmartsheetImpl in thread safe manner.
98
     */
99
    private final AtomicReference<String> assumedUser;
100

101
    /**
102
     * Represents the AtomicReference for change agent
103
     * <p>
104
     * It will be initialized in constructor and will not change afterward.
105
     */
106
    private final AtomicReference<String> changeAgent;
107

108
    /**
109
     * Represents the AtomicReference for the user agent
110
     */
111
    private final AtomicReference<String> userAgent;
112

113
    /**
114
     * Represents the AtomicReference to HomeResources.
115
     * <p>
116
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
117
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
118
     * effectively the underlying value is lazily created in a thread safe manner.
119
     */
120
    private final AtomicReference<HomeResources> home;
121

122
    /**
123
     * Represents the AtomicReference to WorkspaceResources.
124
     * <p>
125
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
126
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
127
     * effectively the underlying value is lazily created in a thread safe manner.
128
     */
129
    private final AtomicReference<WorkspaceResources> workspaces;
130

131
    /**
132
     * Represents the AtomicReference to FolderResources.
133
     * <p>
134
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
135
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
136
     * effectively the underlying value is lazily created in a thread safe manner.
137
     */
138
    private final AtomicReference<FolderResources> folders;
139

140
    /**
141
     * Represents the AtomicReference to SheetResources.
142
     * <p>
143
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
144
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
145
     * effectively the underlying value is lazily created in a thread safe manner.
146
     */
147
    private final AtomicReference<SheetResources> sheets;
148

149
    /**
150
     * Represents the AtomicReference to SightResources
151
     * <p>
152
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
153
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
154
     * effectively the underlying value is lazily created in a thread safe manner.
155
     */
156
    private final AtomicReference<SightResources> sights;
157

158
    /**
159
     * Represents the AtomicReference to UserResources.
160
     * <p>
161
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
162
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
163
     * effectively the underlying value is lazily created in a thread safe manner.
164
     */
165
    private final AtomicReference<UserResources> users;
166

167
    /**
168
     * Represents the AtomicReference to {@link GroupResources}.
169
     * <p>
170
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
171
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
172
     * effectively the underlying value is lazily created in a thread safe manner.
173
     */
174
    private final AtomicReference<GroupResources> groups;
175

176
    /**
177
     * Represents the AtomicReference to SearchResources.
178
     * <p>
179
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
180
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
181
     * effectively the underlying value is lazily created in a thread safe manner.
182
     */
183
    private final AtomicReference<SearchResources> search;
184

185
    /**
186
     * Represents the AtomicReference to ReportResources.
187
     * <p>
188
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
189
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
190
     * effectively the underlying value is lazily created in a thread safe manner.
191
     */
192
    private final AtomicReference<ReportResources> reports;
193

194
    /**
195
     * Represents the AtomicReference for ServerInfoResources.
196
     * <p>
197
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
198
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
199
     * effectively the underlying value is lazily created in a thread safe manner.
200
     */
201
    private final AtomicReference<ServerInfoResources> serverInfo;
202

203
    /**
204
     * Represents the AtomicReference for FavoriteResources.
205
     * <p>
206
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
207
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
208
     * effectively the underlying value is lazily created in a thread safe manner.
209
     */
210
    private final AtomicReference<FavoriteResources> favorites;
211

212
    /**
213
     * Represents the AtomicReference for TokenResources.
214
     * <p>
215
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
216
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
217
     * effectively the underlying value is lazily created in a thread safe manner.
218
     */
219
    private final AtomicReference<TokenResources> tokens;
220

221
    /**
222
     * Represents the AtomicReference for ContactResources.
223
     * <p>
224
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
225
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
226
     * effectively the underlying value is lazily created in a thread safe manner.
227
     */
228
    private final AtomicReference<ContactResources> contacts;
229

230
    /**
231
     * Represents the AtomicReference for ImageUrlResources.
232
     * <p>
233
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
234
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
235
     * effectively the underlying value is lazily created in a thread safe manner.
236
     */
237
    private final AtomicReference<ImageUrlResources> imageUrls;
238

239
    /**
240
     * Represents the AtomicReference for WebhookResources.
241
     * <p>
242
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
243
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
244
     * effectively the underlying value is lazily created in a thread safe manner.
245
     */
246
    private final AtomicReference<WebhookResources> webhooks;
247

248
    /**
249
     * Represents the AtomicReference for PassthroughResources.
250
     * <p>
251
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
252
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
253
     * effectively the underlying value is lazily created in a thread safe manner.
254
     */
255
    private final AtomicReference<PassthroughResources> passthrough;
256

257
    /**
258
     * Represents the AtomicReference for EventResources.
259
     * <p>
260
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
261
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
262
     * effectively the underlying value is lazily created in a thread safe manner.
263
     */
264
    private final AtomicReference<EventResources> events;
265

266
    /**
267
     * Represents the AtomicReference for AssetShareResources.
268
     * <p>
269
     * It will be initialized in constructor and will not change afterward. The underlying value will be initially set
270
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
271
     * effectively the underlying value is lazily created in a thread safe manner.
272
     */
273
    private final AtomicReference<AssetShareResources> assetShares;
274

275
    private static final String INVALID_OPERATION_FOR_CLASS = "Invalid operation for class ";
276

277
    /**
278
     * Create an instance with given server URI, HttpClient (optional) and JsonSerializer (optional)
279
     * <p>
280
     * Exceptions: - IllegalArgumentException : if serverURI/version/accessToken is null/empty
281
     *
282
     * @param baseURI     the server uri
283
     * @param accessToken the access token
284
     */
285
    public SmartsheetImpl(String baseURI, String accessToken) {
286
        this(baseURI, accessToken, null, null);
×
287
    }
×
288

289
    /**
290
     * Create an instance with given server URI, HttpClient (optional) and JsonSerializer (optional)
291
     * <p>
292
     * Exceptions: - IllegalArgumentException : if serverURI/version/accessToken is null/empty
293
     *
294
     * @param baseURI        the server uri
295
     * @param accessToken    the access token
296
     * @param httpClient     the http client (optional)
297
     * @param jsonSerializer the json serializer (optional)
298
     */
299
    public SmartsheetImpl(String baseURI, String accessToken, HttpClient httpClient, JsonSerializer jsonSerializer) {
1✔
300
        Util.throwIfNull(baseURI);
1✔
301
        Util.throwIfEmpty(baseURI);
1✔
302

303
        this.baseURI = URI.create(baseURI);
1✔
304
        this.accessToken = new AtomicReference<>(accessToken);
1✔
305
        this.jsonSerializer = (jsonSerializer == null) ? new JacksonJsonSerializer() : jsonSerializer;
1✔
306
        this.httpClient = (httpClient == null)
1✔
307
                ? new DefaultHttpClient(HttpClients.createDefault(), this.jsonSerializer) : httpClient;
1✔
308
        CleanerUtil.register(this, CleanerUtil.closeQuietly(this.httpClient));
1✔
309

310
        this.assumedUser = new AtomicReference<>(null);
1✔
311
        this.changeAgent = new AtomicReference<>(null);
1✔
312
        this.userAgent = new AtomicReference<>(generateUserAgent(null));
1✔
313

314
        // Initialize resources
315
        this.home = new AtomicReference<>();
1✔
316
        this.workspaces = new AtomicReference<>();
1✔
317
        this.folders = new AtomicReference<>();
1✔
318
        this.sheets = new AtomicReference<>();
1✔
319
        this.sights = new AtomicReference<>();
1✔
320
        this.favorites = new AtomicReference<>();
1✔
321
        this.users = new AtomicReference<>();
1✔
322
        this.groups = new AtomicReference<>();
1✔
323
        this.search = new AtomicReference<>();
1✔
324
        this.reports = new AtomicReference<>();
1✔
325
        this.serverInfo = new AtomicReference<>();
1✔
326
        this.tokens = new AtomicReference<>();
1✔
327
        this.contacts = new AtomicReference<>();
1✔
328
        this.imageUrls = new AtomicReference<>();
1✔
329
        this.webhooks = new AtomicReference<>();
1✔
330
        this.passthrough = new AtomicReference<>();
1✔
331
        this.events = new AtomicReference<>();
1✔
332
        this.assetShares = new AtomicReference<>();
1✔
333
    }
1✔
334

335
    /**
336
     * Getter of corresponding field.
337
     * <p>
338
     * Returns: corresponding field.
339
     *
340
     * @return the base uri
341
     */
342
    URI getBaseURI() {
343
        return baseURI;
1✔
344
    }
345

346
    /**
347
     * Return the access token
348
     *
349
     * @return the access token
350
     */
351
    String getAccessToken() {
352
        return accessToken.get();
1✔
353
    }
354

355
    /**
356
     * Set the access token to use.
357
     * <p>
358
     * Parameters: - accessToken : the access token
359
     * <p>
360
     * Returns: None
361
     * <p>
362
     *
363
     * @param accessToken the new access token
364
     */
365
    public void setAccessToken(String accessToken) {
366
        this.accessToken.set(accessToken);
1✔
367
    }
1✔
368

369
    /**
370
     * Getter of corresponding field.
371
     *
372
     * @return corresponding field
373
     */
374
    JsonSerializer getJsonSerializer() {
375
        return jsonSerializer;
1✔
376
    }
377

378
    /**
379
     * Getter of corresponding field.
380
     *
381
     * @return corresponding field.
382
     */
383
    HttpClient getHttpClient() {
384
        return httpClient;
1✔
385
    }
386

387
    /**
388
     * Return the assumed user.
389
     *
390
     * @return the assumed user
391
     */
392
    String getAssumedUser() {
393
        return assumedUser.get();
1✔
394
    }
395

396
    /**
397
     * Set the email of the user to assume. Null/empty string indicates no user is assumed.
398
     *
399
     * @param assumedUser the email of the user to assume
400
     */
401
    public void setAssumedUser(String assumedUser) {
402
        this.assumedUser.set(assumedUser);
1✔
403
    }
1✔
404

405
    /**
406
     * Return the change agent identifier.
407
     *
408
     * @return the access token
409
     */
410
    String getChangeAgent() {
411
        return changeAgent.get();
1✔
412
    }
413

414
    /**
415
     * Sets the change agent identifier
416
     */
417
    public void setChangeAgent(String changeAgent) {
418
        this.changeAgent.set(changeAgent);
1✔
419
    }
1✔
420

421
    /**
422
     * Return the user agent string
423
     *
424
     * @return the user agent string
425
     */
426
    public String getUserAgent() {
427
        return userAgent.get();
1✔
428
    }
429

430
    /**
431
     * Sets the user agent string
432
     *
433
     * @param userAgent the user agent string
434
     */
435
    public void setUserAgent(String userAgent) {
436
        this.userAgent.set(generateUserAgent(userAgent));
×
437
    }
×
438

439
    /**
440
     * Sets the max retry time if the HttpClient is an instance of DefaultHttpClient
441
     *
442
     * @param maxRetryTimeMillis max retry time
443
     */
444
    public void setMaxRetryTimeMillis(long maxRetryTimeMillis) {
445
        if (this.httpClient instanceof DefaultHttpClient) {
×
446
            ((DefaultHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
447
        } else if (this.httpClient instanceof AndroidHttpClient) {
×
448
            ((AndroidHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
449
        } else {
450
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
451
        }
452
    }
×
453

454
    /**
455
     * set what request/response fields to log in trace-logging
456
     */
457
    public void setTraces(Trace... traces) {
458
        if (this.httpClient instanceof DefaultHttpClient) {
1✔
459
            ((DefaultHttpClient) this.httpClient).setTraces(traces);
1✔
460
        } else {
461
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
462
        }
463
    }
1✔
464

465
    /**
466
     * set whether or not to generate "pretty formatted" JSON in trace-logging
467
     */
468
    public void setTracePrettyPrint(boolean pretty) {
469
        if (this.httpClient instanceof DefaultHttpClient) {
×
470
            ((DefaultHttpClient) this.httpClient).setTracePrettyPrint(pretty);
×
471
        } else {
472
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
473
        }
474
    }
×
475

476
    /**
477
     * Returns the HomeResources instance that provides access to Home resources.
478
     *
479
     * @return the home resources
480
     * @deprecated Home resources have been deprecated and will be removed in a future version.
481
     */
482
    @Deprecated(since = "3.4.0", forRemoval = true)
483
    public HomeResources homeResources() {
484
        if (home.get() == null) {
1✔
485
            home.compareAndSet(null, new HomeResourcesImpl(this));
1✔
486
        }
487
        return home.get();
1✔
488
    }
489

490
    /**
491
     * Returns the WorkspaceResources instance that provides access to Workspace resources.
492
     *
493
     * @return the workspace resources
494
     */
495
    public WorkspaceResources workspaceResources() {
496
        if (workspaces.get() == null) {
1✔
497
            workspaces.compareAndSet(null, new WorkspaceResourcesImpl(this));
1✔
498
        }
499
        return workspaces.get();
1✔
500
    }
501

502
    /**
503
     * Returns the FolderResources instance that provides access to Folder resources.
504
     *
505
     * @return the folder resources
506
     */
507
    public FolderResources folderResources() {
508
        if (folders.get() == null) {
1✔
509
            folders.compareAndSet(null, new FolderResourcesImpl(this));
1✔
510
        }
511
        return folders.get();
1✔
512
    }
513

514
    /**
515
     * Returns the SheetResources instance that provides access to Sheet resources.
516
     *
517
     * @return the sheet resources
518
     */
519
    public SheetResources sheetResources() {
520
        if (sheets.get() == null) {
1✔
521
            sheets.compareAndSet(null, new SheetResourcesImpl(this));
1✔
522
        }
523
        return sheets.get();
1✔
524
    }
525

526
    /**
527
     * Returns the SightResources instance that provides access to Sight resources.
528
     *
529
     * @return the sight resources
530
     */
531
    public SightResources sightResources() {
532
        if (sights.get() == null) {
1✔
533
            sights.compareAndSet(null, new SightResourcesImpl(this));
1✔
534
        }
535
        return sights.get();
1✔
536
    }
537

538
    /**
539
     * Returns the FavoriteResources instance that provides access to Favorite resources.
540
     *
541
     * @return the favorite resources
542
     */
543
    public FavoriteResources favoriteResources() {
544
        if (favorites.get() == null) {
1✔
545
            favorites.compareAndSet(null, new FavoriteResourcesImpl(this));
1✔
546
        }
547
        return favorites.get();
1✔
548
    }
549

550
    /**
551
     * Returns the {@link UserResources} instance that provides access to User resources.
552
     *
553
     * @return the user resources
554
     */
555
    public UserResources userResources() {
556
        if (users.get() == null) {
1✔
557
            users.compareAndSet(null, new UserResourcesImpl(this));
1✔
558
        }
559
        return users.get();
1✔
560
    }
561

562
    /**
563
     * Returns the {@link GroupResources} instance that provides access to User resources.
564
     *
565
     * @return the user resources
566
     */
567
    public GroupResources groupResources() {
568
        if (groups.get() == null) {
×
569
            groups.compareAndSet(null, new GroupResourcesImpl(this));
×
570
        }
571
        return groups.get();
×
572
    }
573

574
    /**
575
     * Returns the {@link SearchResources} instance that provides access to searching resources.
576
     *
577
     * @return the search resources
578
     */
579
    public SearchResources searchResources() {
580
        if (search.get() == null) {
1✔
581
            search.compareAndSet(null, new SearchResourcesImpl(this));
1✔
582
        }
583
        return search.get();
1✔
584
    }
585

586
    /**
587
     * Returns the {@link ReportResources} instance that provides access to Report resources.
588
     *
589
     * @return the report resources
590
     */
591
    public ReportResources reportResources() {
592
        if (reports.get() == null) {
1✔
593
            reports.compareAndSet(null, new ReportResourcesImpl(this));
1✔
594
        }
595
        return reports.get();
1✔
596
    }
597

598
    /**
599
     * Returns the {@link ServerInfoResources} instance that provides access to ServerInfo resources.
600
     *
601
     * @return the ServerInfo resources
602
     */
603
    public ServerInfoResources serverInfoResources() {
604
        if (serverInfo.get() == null) {
×
605
            serverInfo.compareAndSet(null, new ServerInfoResourcesImpl(this));
×
606
        }
607
        return serverInfo.get();
×
608
    }
609

610
    /**
611
     * Returns the TokenResources instance that provides access to token resources.
612
     *
613
     * @return the token resources
614
     */
615
    public TokenResources tokenResources() {
616
        if (tokens.get() == null) {
×
617
            tokens.compareAndSet(null, new TokenResourcesImpl(this));
×
618
        }
619
        return tokens.get();
×
620
    }
621

622
    /**
623
     * Returns the ContactResources instance that provides access to contact resources.
624
     *
625
     * @return the contact resources
626
     */
627
    public ContactResources contactResources() {
628
        if (contacts.get() == null) {
×
629
            contacts.compareAndSet(null, new ContactResourcesImpl(this));
×
630
        }
631
        return contacts.get();
×
632
    }
633

634
    /**
635
     * Returns the ImageUrlResources instance that provides access to image url resources.
636
     *
637
     * @return the image url resources
638
     */
639
    public ImageUrlResources imageUrlResources() {
640
        if (imageUrls.get() == null) {
×
641
            imageUrls.compareAndSet(null, new ImageUrlResourcesImpl(this));
×
642
        }
643
        return imageUrls.get();
×
644
    }
645

646
    /**
647
     * Returns the WebhookResources instance that provides access to webhook resources.
648
     *
649
     * @return the webhook resources
650
     */
651
    public WebhookResources webhookResources() {
652
        if (webhooks.get() == null) {
×
653
            webhooks.compareAndSet(null, new WebhookResourcesImpl(this));
×
654
        }
655
        return webhooks.get();
×
656
    }
657

658
    /**
659
     * Returns the PassthroughResources instance that provides access to passthrough resources.
660
     *
661
     * @return the passthrough resources
662
     */
663
    public PassthroughResources passthroughResources() {
664
        if (passthrough.get() == null) {
×
665
            passthrough.compareAndSet(null, new PassthroughResourcesImpl(this));
×
666
        }
667
        return passthrough.get();
×
668
    }
669

670
    /**
671
     * Returns the EventResources instance that provides access to events resources.
672
     *
673
     * @return the events resources
674
     */
675
    public EventResources eventResources() {
676
        if (events.get() == null) {
×
677
            events.compareAndSet(null, new EventResourcesImpl(this));
×
678
        }
679
        return events.get();
×
680
    }
681

682
    /**
683
     * Returns the AssetShareResources instance that provides access to Asset Share resources.
684
     *
685
     * @return the asset share resources
686
     */
687
    public AssetShareResources assetShareResources() {
688
        if (assetShares.get() == null) {
×
689
            assetShares.compareAndSet(null, new AssetShareResourcesImpl(this));
×
690
        }
691
        return assetShares.get();
×
692
    }
693

694
    /**
695
     * Compose a User-Agent string that represents this version of the SDK (along with platform info)
696
     *
697
     * @return a User-Agent string
698
     */
699
    private String generateUserAgent(String userAgent) {
700
        String title = null;
1✔
701
        String thisVersion = null;
1✔
702

703
        if (userAgent == null) {
1✔
704
            StackTraceElement[] callers = Thread.currentThread().getStackTrace();
1✔
705
            String module = null;
1✔
706
            String callerClass = null;
1✔
707
            int stackIdx;
708
            for (stackIdx = callers.length - 1; stackIdx >= 0; stackIdx--) {
1✔
709
                callerClass = callers[stackIdx].getClassName();
1✔
710
                try {
711
                    Class<?> clazz = Class.forName(callerClass);
1✔
712
                    ClassLoader classLoader = clazz.getClassLoader();
1✔
713
                    // skip JRE classes
714
                    if (classLoader == null) {
1✔
715
                        continue;
×
716
                    }
717
                    String classFilePath = callerClass.replace(".", "/") + ".class";
1✔
718
                    URL classUrl = classLoader.getResource(classFilePath);
1✔
719
                    if (classUrl != null) {
1✔
720
                        String classUrlPath = classUrl.getPath();
1✔
721
                        int jarSeparator = classUrlPath.indexOf('!');
1✔
722
                        if (jarSeparator > 0) {
1✔
723
                            module = classUrlPath.substring(0, jarSeparator);
1✔
724
                            // extract the last path element (the jar name only)
725
                            module = module.substring(module.lastIndexOf('/') + 1);
1✔
726
                            break;
1✔
727
                        }
728
                    }
729
                } catch (Exception ex) {
×
730
                    // Empty Catch Block
731
                }
×
732
            }
733
            userAgent = module + "!" + callerClass;
1✔
734
        }
735
        try {
736
            final Properties properties = new Properties();
1✔
737
            properties.load(this.getClass().getClassLoader().getResourceAsStream("sdk.properties"));
1✔
738
            thisVersion = properties.getProperty("sdk.version");
1✔
739
            title = properties.getProperty("sdk.name");
1✔
740
        } catch (IOException e) {
×
741
            // Empty Catch Block
742
        }
1✔
743
        return title + "/" + thisVersion + "/" + userAgent + "/" + System.getProperty("os.name") + " " +
1✔
744
                System.getProperty("java.vm.name") + " " + System.getProperty("java.vendor") + " " +
1✔
745
                System.getProperty("java.version");
1✔
746
    }
747
}
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