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

smartsheet / smartsheet-java-sdk / #41

24 Aug 2023 04:59PM UTC coverage: 50.458% (+0.01%) from 50.444%
#41

push

github-actions

web-flow
Fix Checkstyle Violations in "Impl" Classes (#53)

241 of 241 new or added lines in 32 files covered. (100.0%)

3417 of 6772 relevant lines covered (50.46%)

0.5 hits per line

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

69.33
/src/main/java/com/smartsheet/api/internal/SmartsheetImpl.java
1
package com.smartsheet.api.internal;
2

3
/*
4
 * #[license]
5
 * Smartsheet SDK for Java
6
 * %%
7
 * Copyright (C) 2023 Smartsheet
8
 * %%
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *      http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 * %[license]
21
 */
22

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

51
import java.io.IOException;
52
import java.net.URI;
53
import java.net.URL;
54
import java.util.Properties;
55
import java.util.concurrent.atomic.AtomicReference;
56

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

65
    /**
66
     * Represents the base URI of the Smartsheet REST API.
67
     * <p>
68
     * It will be initialized in constructor and will not change afterwards.
69
     */
70
    private URI baseURI;
71

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

81
    /**
82
     * Represents the HttpClient.
83
     * <p>
84
     * It will be initialized in constructor and will not change afterwards.
85
     */
86
    private final HttpClient httpClient;
87

88
    /**
89
     * Represents the JsonSerializer.
90
     * <p>
91
     * It will be initialized in constructor and will not change afterwards.
92
     */
93
    private JsonSerializer jsonSerializer;
94

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

104
    /**
105
     * Represents the AtomicReference for change agent
106
     * <p>
107
     * It will be initialized in constructor and will not change afterwards.
108
     *
109
     */
110
    private final AtomicReference<String> changeAgent;
111

112
    /**
113
     * Represents the AtomicReference for the user agent
114
     */
115
    private final AtomicReference<String> userAgent;
116

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

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

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

144
    /**
145
     * Represents the AtomicReference to TemplateResources.
146
     * <p>
147
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
148
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
149
     * effectively the underlying value is lazily created in a thread safe manner.
150
     */
151
    private AtomicReference<TemplateResources> templates;
152

153
    /**
154
     * Represents the AtomicReference to SheetResources.
155
     * <p>
156
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
157
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
158
     * effectively the underlying value is lazily created in a thread safe manner.
159
     */
160
    private AtomicReference<SheetResources> sheets;
161

162
    /**
163
     * Represents the AtomicReference to SightResources
164
     * <p>
165
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
166
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
167
     * effectively the underlying value is lazily created in a thread safe manner.
168
     */
169
    private AtomicReference<SightResources> sights;
170

171
    /**
172
     * Represents the AtomicReference to UserResources.
173
     * <p>
174
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
175
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
176
     * effectively the underlying value is lazily created in a thread safe manner.
177
     */
178
    private AtomicReference<UserResources> users;
179

180
    /**
181
     * Represents the AtomicReference to {@link GroupResources}.
182
     * <p>
183
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
184
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
185
     * effectively the underlying value is lazily created in a thread safe manner.
186
     */
187
    private AtomicReference<GroupResources> groups;
188

189
    /**
190
     * Represents the AtomicReference to SearchResources.
191
     * <p>
192
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
193
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
194
     * effectively the underlying value is lazily created in a thread safe manner.
195
     */
196
    private AtomicReference<SearchResources> search;
197

198
    /**
199
     * Represents the AtomicReference to ReportResources.
200
     * <p>
201
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
202
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
203
     * effectively the underlying value is lazily created in a thread safe manner.
204
     */
205
    private AtomicReference<ReportResources> reports;
206

207
    /**
208
     * Represents the AtomicReference for ServerInfoResources.
209
     * <p>
210
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
211
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
212
     * effectively the underlying value is lazily created in a thread safe manner.
213
     */
214
    private final AtomicReference<ServerInfoResources> serverInfo;
215

216
    /**
217
     * Represents the AtomicReference for FavoriteResources.
218
     * <p>
219
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
220
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
221
     * effectively the underlying value is lazily created in a thread safe manner.
222
     */
223
    private final AtomicReference<FavoriteResources> favorites;
224

225
    /**
226
     * Represents the AtomicReference for TokenResources.
227
     * <p>
228
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
229
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
230
     * effectively the underlying value is lazily created in a thread safe manner.
231
     */
232
    private final AtomicReference<TokenResources> tokens;
233

234
    /**
235
     * Represents the AtomicReference for ContactResources.
236
     * <p>
237
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
238
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
239
     * effectively the underlying value is lazily created in a thread safe manner.
240
     */
241
    private final AtomicReference<ContactResources> contacts;
242

243
    /**
244
     * Represents the AtomicReference for ImageUrlResources.
245
     * <p>
246
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
247
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
248
     * effectively the underlying value is lazily created in a thread safe manner.
249
     */
250
    private final AtomicReference<ImageUrlResources> imageUrls;
251

252
    /**
253
     * Represents the AtomicReference for WebhookResources.
254
     * <p>
255
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
256
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
257
     * effectively the underlying value is lazily created in a thread safe manner.
258
     */
259
    private final AtomicReference<WebhookResources> webhooks;
260

261
    /**
262
     * Represents the AtomicReference for PassthroughResources.
263
     * <p>
264
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
265
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
266
     * effectively the underlying value is lazily created in a thread safe manner.
267
     */
268
    private final AtomicReference<PassthroughResources> passthrough;
269

270
    /**
271
     * Represents the AtomicReference for EventResources.
272
     * <p>
273
     * It will be initialized in constructor and will not change afterwards. The underlying value will be initially set
274
     * as null, and will be initialized to non-null at the first time it is accessed via corresponding getter, therefore
275
     * effectively the underlying value is lazily created in a thread safe manner.
276
     */
277
    private final AtomicReference<EventResources> events;
278

279
    private static final String INVALID_OPERATION_FOR_CLASS = "Invalid operation for class ";
280

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

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

307
        this.baseURI = URI.create(baseURI);
1✔
308
        this.accessToken = new AtomicReference<>(accessToken);
1✔
309
        this.jsonSerializer = (jsonSerializer == null) ? new JacksonJsonSerializer() : jsonSerializer;
1✔
310
        this.httpClient = (httpClient == null)
1✔
311
                ? new DefaultHttpClient(HttpClients.createDefault(), this.jsonSerializer) : httpClient;
1✔
312
        this.assumedUser = new AtomicReference<>(null);
1✔
313
        this.changeAgent = new AtomicReference<>(null);
1✔
314
        this.userAgent = new AtomicReference<>(generateUserAgent(null));
1✔
315

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

337
    /**
338
     * Finalize the object, this method is overridden to close the HttpClient.
339
     *
340
     * @throws IOException Signals that an I/O exception has occurred.
341
     */
342
    protected void finalize() throws IOException {
343
        this.httpClient.close();
1✔
344
    }
1✔
345

346
    /**
347
     * Getter of corresponding field.
348
     * <p>
349
     * Returns: corresponding field.
350
     *
351
     * @return the base uri
352
     */
353
    URI getBaseURI() {
354
        return baseURI;
1✔
355
    }
356

357
    /**
358
     * Return the access token
359
     *
360
     * @return the access token
361
     */
362
    String getAccessToken() {
363
        return accessToken.get();
1✔
364
    }
365

366
    /**
367
     * Set the access token to use.
368
     * <p>
369
     * Parameters: - accessToken : the access token
370
     * <p>
371
     * Returns: None
372
     * <p>
373
     *
374
     * @param accessToken the new access token
375
     */
376
    public void setAccessToken(String accessToken) {
377
        this.accessToken.set(accessToken);
1✔
378
    }
1✔
379

380
    /**
381
     * Getter of corresponding field.
382
     *
383
     * @return corresponding field
384
     */
385
    JsonSerializer getJsonSerializer() {
386
        return jsonSerializer;
1✔
387
    }
388

389
    /**
390
     * Getter of corresponding field.
391
     *
392
     * @return corresponding field.
393
     */
394
    HttpClient getHttpClient() {
395
        return httpClient;
1✔
396
    }
397

398
    /**
399
     * Return the assumed user.
400
     *
401
     * @return the assumed user
402
     */
403
    String getAssumedUser() {
404
        return assumedUser.get();
1✔
405
    }
406

407
    /**
408
     * Set the email of the user to assume. Null/empty string indicates no user is assumed.
409
     *
410
     * @param assumedUser the email of the user to assume
411
     */
412
    public void setAssumedUser(String assumedUser) {
413
        this.assumedUser.set(assumedUser);
1✔
414
    }
1✔
415

416
    /**
417
     * Return the change agent identifier.
418
     *
419
     * @return the access token
420
     */
421
    String getChangeAgent() {
422
        return changeAgent.get();
1✔
423
    }
424

425
    /**
426
     * Sets the change agent identifier
427
     *
428
     */
429
    public void setChangeAgent(String changeAgent) {
430
        this.changeAgent.set(changeAgent);
1✔
431
    }
1✔
432

433
    /**
434
     * Return the user agent string
435
     *
436
     * @return the user agent string
437
     */
438
    public String getUserAgent() {
439
        return userAgent.get();
1✔
440
    }
441

442
    /**
443
     * Sets the user agent string
444
     *
445
     * @param userAgent the user agent string
446
     */
447
    public void setUserAgent(String userAgent) {
448
        this.userAgent.set(generateUserAgent(userAgent));
×
449
    }
×
450

451
    /**
452
     * Sets the max retry time if the HttpClient is an instance of DefaultHttpClient
453
     *
454
     * @param maxRetryTimeMillis max retry time
455
     */
456
    public void setMaxRetryTimeMillis(long maxRetryTimeMillis) {
457
        if (this.httpClient instanceof DefaultHttpClient) {
×
458
            ((DefaultHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
459
        } else if (this.httpClient instanceof AndroidHttpClient) {
×
460
            ((AndroidHttpClient) this.httpClient).setMaxRetryTimeMillis(maxRetryTimeMillis);
×
461
        } else {
462
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
463
        }
464
    }
×
465

466
    /** set what request/response fields to log in trace-logging */
467
    public void setTraces(Trace... traces) {
468
        if (this.httpClient instanceof DefaultHttpClient) {
×
469
            ((DefaultHttpClient) this.httpClient).setTraces(traces);
×
470
        } else {
471
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
472
        }
473
    }
×
474

475
    /** set whether or not to generate "pretty formatted" JSON in trace-logging */
476
    public void setTracePrettyPrint(boolean pretty) {
477
        if (this.httpClient instanceof DefaultHttpClient) {
×
478
            ((DefaultHttpClient) this.httpClient).setTracePrettyPrint(pretty);
×
479
        } else {
480
            throw new UnsupportedOperationException(INVALID_OPERATION_FOR_CLASS + this.httpClient.getClass());
×
481
        }
482
    }
×
483

484
    /**
485
     * Returns the HomeResources instance that provides access to Home resources.
486
     *
487
     * @return the home resources
488
     */
489
    public HomeResources homeResources() {
490
        if (home.get() == null) {
1✔
491
            home.compareAndSet(null, new HomeResourcesImpl(this));
1✔
492
        }
493
        return home.get();
1✔
494
    }
495

496
    /**
497
     * Returns the WorkspaceResources instance that provides access to Workspace resources.
498
     *
499
     * @return the workspace resources
500
     */
501
    public WorkspaceResources workspaceResources() {
502
        if (workspaces.get() == null) {
1✔
503
            workspaces.compareAndSet(null, new WorkspaceResourcesImpl(this));
1✔
504
        }
505
        return workspaces.get();
1✔
506
    }
507

508
    /**
509
     * Returns the FolderResources instance that provides access to Folder resources.
510
     *
511
     * @return the folder resources
512
     */
513
    public FolderResources folderResources() {
514
        if (folders.get() == null) {
1✔
515
            folders.compareAndSet(null, new FolderResourcesImpl(this));
1✔
516
        }
517
        return folders.get();
1✔
518
    }
519

520
    /**
521
     * Returns the TemplateResources instance that provides access to Template resources.
522
     *
523
     * @return the template resources
524
     */
525
    public TemplateResources templateResources() {
526
        if (templates.get() == null) {
1✔
527
            templates.compareAndSet(null, new TemplateResourcesImpl(this));
1✔
528
        }
529
        return templates.get();
1✔
530
    }
531

532
    /**
533
     * Returns the SheetResources instance that provides access to Sheet resources.
534
     *
535
     * @return the sheet resources
536
     */
537
    public SheetResources sheetResources() {
538
        if (sheets.get() == null) {
1✔
539
            sheets.compareAndSet(null, new SheetResourcesImpl(this));
1✔
540
        }
541
        return sheets.get();
1✔
542
    }
543

544
    /**
545
     * Returns the SightResources instance that provides access to Sight resources.
546
     *
547
     * @return the sight resources
548
     */
549
    public SightResources sightResources() {
550
        if (sights.get() == null) {
1✔
551
            sights.compareAndSet(null, new SightResourcesImpl(this));
1✔
552
        }
553
        return sights.get();
1✔
554
    }
555

556
    /**
557
     * Returns the FavoriteResources instance that provides access to Favorite resources.
558
     *
559
     * @return the favorite resources
560
     */
561
    public FavoriteResources favoriteResources() {
562
        if (favorites.get() == null) {
1✔
563
            favorites.compareAndSet(null, new FavoriteResourcesImpl(this));
1✔
564
        }
565
        return favorites.get();
1✔
566
    }
567

568
    /**
569
     * Returns the {@link UserResources} instance that provides access to User resources.
570
     *
571
     * @return the user resources
572
     */
573
    public UserResources userResources() {
574
        if (users.get() == null) {
1✔
575
            users.compareAndSet(null, new UserResourcesImpl(this));
1✔
576
        }
577
        return users.get();
1✔
578
    }
579

580
    /**
581
     * Returns the {@link GroupResources} instance that provides access to User resources.
582
     *
583
     * @return the user resources
584
     */
585
    public GroupResources groupResources() {
586
        if (groups.get() == null) {
×
587
            groups.compareAndSet(null, new GroupResourcesImpl(this));
×
588
        }
589
        return groups.get();
×
590
    }
591

592
    /**
593
     * Returns the {@link SearchResources} instance that provides access to searching resources.
594
     *
595
     * @return the search resources
596
     */
597
    public SearchResources searchResources() {
598
        if (search.get() == null) {
1✔
599
            search.compareAndSet(null, new SearchResourcesImpl(this));
1✔
600
        }
601
        return search.get();
1✔
602
    }
603

604
    /**
605
     * Returns the {@link ReportResources} instance that provides access to Report resources.
606
     *
607
     * @return the report resources
608
     */
609
    public ReportResources reportResources() {
610
        if (reports.get() == null) {
1✔
611
            reports.compareAndSet(null, new ReportResourcesImpl(this));
1✔
612
        }
613
        return reports.get();
1✔
614
    }
615

616
    /**
617
     * Returns the {@link ServerInfoResources} instance that provides access to ServerInfo resources.
618
     *
619
     * @return the ServerInfo resources
620
     */
621
    public ServerInfoResources serverInfoResources() {
622
        if (serverInfo.get() == null) {
×
623
            serverInfo.compareAndSet(null, new ServerInfoResourcesImpl(this));
×
624
        }
625
        return serverInfo.get();
×
626
    }
627

628
    /**
629
     * Returns the TokenResources instance that provides access to token resources.
630
     *
631
     * @return the token resources
632
     */
633
    public TokenResources tokenResources() {
634
        if (tokens.get() == null) {
×
635
            tokens.compareAndSet(null, new TokenResourcesImpl(this));
×
636
        }
637
        return tokens.get();
×
638
    }
639

640
    /**
641
     * Returns the ContactResources instance that provides access to contact resources.
642
     *
643
     * @return the contact resources
644
     */
645
    public ContactResources contactResources() {
646
        if (contacts.get() == null) {
×
647
            contacts.compareAndSet(null, new ContactResourcesImpl(this));
×
648
        }
649
        return contacts.get();
×
650
    }
651

652
    /**
653
     * Returns the ImageUrlResources instance that provides access to image url resources.
654
     *
655
     * @return the image url resources
656
     */
657
    public ImageUrlResources imageUrlResources() {
658
        if (imageUrls.get() == null) {
×
659
            imageUrls.compareAndSet(null, new ImageUrlResourcesImpl(this));
×
660
        }
661
        return imageUrls.get();
×
662
    }
663

664
    /**
665
     * Returns the WebhookResources instance that provides access to webhook resources.
666
     *
667
     * @return the webhook resources
668
     */
669
    public WebhookResources webhookResources() {
670
        if (webhooks.get() == null) {
×
671
            webhooks.compareAndSet(null, new WebhookResourcesImpl(this));
×
672
        }
673
        return webhooks.get();
×
674
    }
675

676
    /**
677
     * Returns the PassthroughResources instance that provides access to passthrough resources.
678
     *
679
     * @return the passthrough resources
680
     */
681
    public PassthroughResources passthroughResources() {
682
        if (passthrough.get() == null) {
×
683
            passthrough.compareAndSet(null, new PassthroughResourcesImpl(this));
×
684
        }
685
        return passthrough.get();
×
686
    }
687

688
    /**
689
     * Returns the EventResources instance that provides access to events resources.
690
     *
691
     * @return the events resources
692
     */
693
    public EventResources eventResources() {
694
        if (events.get() == null) {
×
695
            events.compareAndSet(null, new EventResourcesImpl(this));
×
696
        }
697
        return events.get();
×
698
    }
699

700
    /**
701
     * Compose a User-Agent string that represents this version of the SDK (along with platform info)
702
     *
703
     * @return a User-Agent string
704
     */
705
    private String generateUserAgent(String userAgent) {
706
        String title = null;
1✔
707
        String thisVersion = null;
1✔
708

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