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

nats-io / nats.java / #2178

11 Sep 2025 03:48PM UTC coverage: 95.066% (-0.04%) from 95.107%
#2178

push

github

web-flow
Merge pull request #1417 from nats-io/scoket-read-timeout

Socket Read Timeout remove validation

2 of 2 new or added lines in 1 file covered. (100.0%)

7 existing lines in 3 files now uncovered.

11966 of 12587 relevant lines covered (95.07%)

0.95 hits per line

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

96.07
/src/main/java/io/nats/client/Options.java
1
// Copyright 2015-2018 The NATS Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at:
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package io.nats.client;
15

16
import io.nats.client.impl.*;
17
import io.nats.client.support.*;
18
import org.jspecify.annotations.NonNull;
19

20
import javax.net.ssl.SSLContext;
21
import java.io.File;
22
import java.io.IOException;
23
import java.lang.reflect.Constructor;
24
import java.net.Proxy;
25
import java.net.URI;
26
import java.net.URISyntaxException;
27
import java.nio.CharBuffer;
28
import java.nio.file.Files;
29
import java.nio.file.Paths;
30
import java.security.GeneralSecurityException;
31
import java.security.NoSuchAlgorithmException;
32
import java.time.Duration;
33
import java.time.LocalDateTime;
34
import java.time.format.DateTimeFormatter;
35
import java.time.format.DateTimeParseException;
36
import java.util.*;
37
import java.util.concurrent.*;
38
import java.util.concurrent.atomic.AtomicInteger;
39
import java.util.function.Supplier;
40

41
import static io.nats.client.support.Encoding.*;
42
import static io.nats.client.support.NatsConstants.*;
43
import static io.nats.client.support.SSLUtils.DEFAULT_TLS_ALGORITHM;
44
import static io.nats.client.support.Validator.*;
45

46
/**
47
 * The Options class specifies the connection options for a new NATs connection, including the default options.
48
 * Options are created using a {@link Options.Builder Builder}.
49
 * This class and the builder associated with it, is basically a long list of parameters. The documentation attempts
50
 * to clarify the value of each parameter in place on the builder and here, but it may be easier to read the documentation
51
 * starting with the {@link Options.Builder Builder}, since it has a simple list of methods that configure the connection.
52
 */
53
public class Options {
54
    // ----------------------------------------------------------------------------------------------------
55
    // NOTE TO DEVS!!! To add an option, you have to address:
56
    // ----------------------------------------------------------------------------------------------------
57
    // CONSTANTS * optionally add a default value constant
58
    // ENVIRONMENT PROPERTIES * always add an environment property. Constant always starts with PFX, but code accepts without
59
    // PROTOCOL CONNECT OPTION CONSTANTS * not related to options, but here because Options code uses them
60
    // CLASS VARIABLES * add a variable to the class
61
    // BUILDER VARIABLES * add a variable in builder
62
    // BUILD CONSTRUCTOR PROPS * update build props constructor to read new props
63
    // BUILDER METHODS * add a chainable method in builder for new variable
64
    // BUILD IMPL * update build() implementation if needed
65
    // BUILDER COPY CONSTRUCTOR * update builder constructor to ensure new variables are set
66
    // CONSTRUCTOR * update constructor to ensure new variables are set from builder
67
    // GETTERS * update getter to be able to retrieve class variable value
68
    // HELPER FUNCTIONS * just helpers
69
    // ----------------------------------------------------------------------------------------------------
70
    // README - if you add a property or change its comment, add it to or update the readme
71
    // ----------------------------------------------------------------------------------------------------
72

73
    // ----------------------------------------------------------------------------------------------------
74
    // CONSTANTS
75
    // ----------------------------------------------------------------------------------------------------
76
    /**
77
     * Default server URL. This property is defined as {@value}
78
     */
79
    public static final String DEFAULT_URL = "nats://localhost:4222";
80

81
    /**
82
     * Default server port. This property is defined as {@value}
83
     */
84
    public static final int DEFAULT_PORT = NatsConstants.DEFAULT_PORT;
85

86
    /**
87
     * Default maximum number of reconnect attempts, see {@link #getMaxReconnect() getMaxReconnect()}.
88
     * This property is defined as {@value}
89
     */
90
    public static final int DEFAULT_MAX_RECONNECT = 60;
91

92
    /**
93
     * Default wait time before attempting reconnection to the same server, see {@link #getReconnectWait() getReconnectWait()}.
94
     * This property is defined as 2000 milliseconds (2 seconds).
95
     */
96
    public static final Duration DEFAULT_RECONNECT_WAIT = Duration.ofMillis(2000);
1✔
97

98
    /**
99
     * Default wait time before attempting reconnection to the same server, see {@link #getReconnectJitter() getReconnectJitter()}.
100
     * This property is defined as 100 milliseconds.
101
     */
102
    public static final Duration DEFAULT_RECONNECT_JITTER = Duration.ofMillis(100);
1✔
103

104
    /**
105
     * Default wait time before attempting reconnection to the same server, see {@link #getReconnectJitterTls() getReconnectJitterTls()}.
106
     * This property is defined as 1000 milliseconds (1 second).
107
     */
108
    public static final Duration DEFAULT_RECONNECT_JITTER_TLS = Duration.ofMillis(1000);
1✔
109

110
    /**
111
     * Default connection timeout, see {@link #getConnectionTimeout() getConnectionTimeout()}.
112
     * This property is defined as 2 seconds.
113
     */
114
    public static final Duration DEFAULT_CONNECTION_TIMEOUT = Duration.ofSeconds(2);
1✔
115

116
    /**
117
     * Default socket write timeout, see {@link #getSocketWriteTimeout() getSocketWriteTimeout()}.
118
     * This property is defined as 1 minute
119
     */
120
    public static final Duration DEFAULT_SOCKET_WRITE_TIMEOUT = Duration.ofMinutes(1);
1✔
121

122
    /**
123
     * @deprecated No longer enforcing a minimum compared to the connection timeout
124
     */
125
    @Deprecated
126
    public static final long MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
127

128
    /**
129
     * @deprecated No longer enforcing a minimum
130
     */
131
    @Deprecated
132
    public static final long MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
133

134
    /**
135
     * Default server ping interval. The client will send a ping to the server on this interval to insure liveness.
136
     * The server may send pings to the client as well, these are handled automatically by the library,
137
     * see {@link #getPingInterval() getPingInterval()}.
138
     * <p>A value of {@code <=0} means disabled.</p>
139
     * <p>This property is defined as 2 minutes.</p>
140
     */
141
    public static final Duration DEFAULT_PING_INTERVAL = Duration.ofMinutes(2);
1✔
142

143
    /**
144
     * Default interval to clean up cancelled/timed out requests.
145
     * A timer is used to clean up futures that were handed out but never completed
146
     * via a message, {@link #getRequestCleanupInterval() getRequestCleanupInterval()}.
147
     * <p>This property is defined as 5 seconds.</p>
148
     */
149
    public static final Duration DEFAULT_REQUEST_CLEANUP_INTERVAL = Duration.ofSeconds(5);
1✔
150

151
    /**
152
     * Default maximum number of pings have not received a response allowed by the
153
     * client, {@link #getMaxPingsOut() getMaxPingsOut()}.
154
     * <p>This property is defined as {@value}</p>
155
     */
156
    public static final int DEFAULT_MAX_PINGS_OUT = 2;
157

158
    /**
159
     * Default SSL protocol used to create an SSLContext if the {@link #PROP_SECURE
160
     * secure property} is used.
161
     * <p>This property is defined as {@value}</p>
162
     */
163
    public static final String DEFAULT_SSL_PROTOCOL = "TLSv1.2";
164

165
    /**
166
     * Default of pending message buffer that is used for buffering messages that
167
     * are published during a disconnect/reconnect, {@link #getReconnectBufferSize() getReconnectBufferSize()}.
168
     * <p>This property is defined as {@value} bytes, 8 * 1024 * 1024.</p>
169
     */
170
    public static final int DEFAULT_RECONNECT_BUF_SIZE = 8_388_608;
171

172
    /**
173
     * The default length, {@value} bytes, the client will allow in an
174
     *  outgoing protocol control line, {@link #getMaxControlLine() getMaxControlLine()}.
175
     * <p>This value is configurable on the server, and should be set here to match.</p>
176
     */
177
    public static final int DEFAULT_MAX_CONTROL_LINE = 4096;
178

179
    /**
180
     * Default dataport class, which will use a TCP socket, {@link #getDataPortType() getDataPortType()}.
181
     * <p><em>This option is currently provided only for testing, and experimentation, the default
182
     * should be used in almost all cases.</em></p>
183
     */
184
    public static final String DEFAULT_DATA_PORT_TYPE = SocketDataPort.class.getCanonicalName();
1✔
185

186
    /**
187
     * Default size for buffers in the connection, not as available as other settings,
188
     * this is primarily changed for testing, {@link #getBufferSize() getBufferSize()}.
189
     */
190
    public static final int DEFAULT_BUFFER_SIZE = 64 * 1024;
191

192
    /**
193
     * Default thread name prefix. Used by the default executor when creating threads.
194
     * This property is defined as {@value}
195
     */
196
    public static final String DEFAULT_THREAD_NAME_PREFIX = "nats";
197

198
    /**
199
     * Default prefix used for inboxes, you can change this to manage authorization of subjects.
200
     * See {@link #getInboxPrefix() getInboxPrefix()}, the . is required but will be added if missing.
201
     */
202
    public static final String DEFAULT_INBOX_PREFIX = "_INBOX.";
203

204
    /**
205
     * This value is used internally to limit the number of messages sent in a single network I/O.
206
     * The value returned by {@link #getBufferSize() getBufferSize()} is used first, but if the buffer
207
     * size is large and the message sizes are small, this limit comes into play.
208
     * The choice of 1000 is arbitrary and based on testing across several operating systems. Use buffer
209
     * size for tuning.
210
     */
211
    public static final int MAX_MESSAGES_IN_NETWORK_BUFFER = 1000;
212

213
    /**
214
     * This value is used internally to limit the number of messages allowed in the outgoing queue. When
215
     * this limit is reached, publish requests will be blocked until the queue can clear.
216
     * Because this value is in messages, the memory size associated with this value depends on the actual
217
     * size of messages. If 0 byte messages are used, then DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE will take up the minimal
218
     * space. If 1024 byte messages are used then approximately 5Mb is used for the queue (plus overhead for subjects, etc..)
219
     * We are using messages, not bytes, to allow a simplification in the underlying library, and use LinkedBlockingQueue as
220
     * the core element in the queue.
221
     */
222
    public static final int DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE = 5000;
223

224
    /**
225
     * This value is used internally to discard messages when the outgoing queue is full.
226
     * See {@link #DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE}
227
     */
228
    public static final boolean DEFAULT_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL = false;
229

230
    /**
231
     * Default supplier for creating a single-threaded executor service.
232
     */
233
    public static final Supplier<ExecutorService> DEFAULT_SINGLE_THREAD_EXECUTOR = Executors::newSingleThreadExecutor;
1✔
234

235
    // ----------------------------------------------------------------------------------------------------
236
    // ENVIRONMENT PROPERTIES
237
    // ----------------------------------------------------------------------------------------------------
238
    static final String PFX = "io.nats.client.";
239
    static final int PFX_LEN = PFX.length();
1✔
240

241
    /**
242
     * Property used to configure a builder from a Properties object. {@value}, see
243
     * {@link Builder#connectionListener(ConnectionListener) connectionListener}.
244
     */
245
    public static final String PROP_CONNECTION_CB = PFX + "callback.connection";
246
    /**
247
     * Property used to configure a builder from a Properties object. {@value}, see
248
     * {@link Builder#dataPortType(String) dataPortType}.
249
     */
250
    public static final String PROP_DATA_PORT_TYPE = PFX + "dataport.type";
251
    /**
252
     * Property used to configure a builder from a Properties object. {@value}, see
253
     * {@link Builder#errorListener(ErrorListener) errorListener}.
254
     */
255
    public static final String PROP_ERROR_LISTENER = PFX + "callback.error";
256
    /**
257
     * Property used to configure a builder from a Properties object. {@value}, see
258
     * {@link Builder#timeTraceLogger(TimeTraceLogger) timeTraceLogger}.
259
     */
260
    public static final String PROP_TIME_TRACE_LOGGER = PFX + "time.trace";
261
    /**
262
     * Property used to configure a builder from a Properties object. {@value}, see
263
     * {@link Builder#statisticsCollector(StatisticsCollector) statisticsCollector}.
264
     */
265
    public static final String PROP_STATISTICS_COLLECTOR = PFX + "statisticscollector";
266
    /**
267
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#maxPingsOut(int) maxPingsOut}.
268
     */
269
    public static final String PROP_MAX_PINGS = PFX + "maxpings";
270
    /**
271
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#pingInterval(Duration)
272
     * pingInterval}.
273
     */
274
    public static final String PROP_PING_INTERVAL = PFX + "pinginterval";
275
    /**
276
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#requestCleanupInterval(Duration)
277
     * requestCleanupInterval}.
278
     */
279
    public static final String PROP_CLEANUP_INTERVAL = PFX + "cleanupinterval";
280
    /**
281
     * Property used to configure a builder from a Properties object. {@value}, see
282
     * {@link Builder#connectionTimeout(Duration) connectionTimeout}.
283
     */
284
    public static final String PROP_CONNECTION_TIMEOUT = PFX + "timeout";
285
    /**
286
     * Property used to configure a builder from a Properties object. {@value}, see
287
     * {@link Builder#socketReadTimeoutMillis(int) socketReadTimeoutMillis}.
288
     */
289
    public static final String PROP_SOCKET_READ_TIMEOUT_MS = PFX + "socket.read.timeout.ms";
290
    /**
291
     * Property used to configure a builder from a Properties object. {@value}, see
292
     * {@link Builder#socketWriteTimeout(long) socketWriteTimeout}.
293
     */
294
    public static final String PROP_SOCKET_WRITE_TIMEOUT = PFX + "socket.write.timeout";
295
    /**
296
     * Property used to configure a builder from a Properties object. {@value}, see
297
     * {@link Builder#socketSoLinger(int) socketSoLinger}.
298
     */
299
    public static final String PROP_SOCKET_SO_LINGER = PFX + "socket.so.linger";
300
    /**
301
     * Property used to configure a builder from a Properties object. {@value}, see
302
     * {@link Builder#reconnectBufferSize(long) reconnectBufferSize}.
303
     */
304
    public static final String PROP_RECONNECT_BUF_SIZE = PFX + "reconnect.buffer.size";
305
    /**
306
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#reconnectWait(Duration)
307
     * reconnectWait}.
308
     */
309
    public static final String PROP_RECONNECT_WAIT = PFX + "reconnect.wait";
310
    /**
311
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#maxReconnects(int)
312
     * maxReconnects}.
313
     */
314
    public static final String PROP_MAX_RECONNECT = PFX + "reconnect.max";
315
    /**
316
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#reconnectJitter(Duration)
317
     * reconnectJitter}.
318
     */
319
    public static final String PROP_RECONNECT_JITTER = PFX + "reconnect.jitter";
320
    /**
321
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#reconnectJitterTls(Duration)
322
     * reconnectJitterTls}.
323
     */
324
    public static final String PROP_RECONNECT_JITTER_TLS = PFX + "reconnect.jitter.tls";
325
    /**
326
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#pedantic() pedantic}.
327
     */
328
    public static final String PROP_PEDANTIC = PFX + "pedantic";
329
    /**
330
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#verbose() verbose}.
331
     */
332
    public static final String PROP_VERBOSE = PFX + "verbose";
333
    /**
334
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#noEcho() noEcho}.
335
     */
336
    public static final String PROP_NO_ECHO = PFX + "noecho";
337
    /**
338
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#noHeaders() noHeaders}.
339
     */
340
    public static final String PROP_NO_HEADERS = PFX + "noheaders";
341
    /**
342
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#connectionName(String)
343
     * connectionName}.
344
     */
345
    public static final String PROP_CONNECTION_NAME = PFX + "name";
346
    /**
347
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#noNoResponders() noNoResponders}.
348
     */
349
    public static final String PROP_NO_NORESPONDERS = PFX + "nonoresponders";
350
    /**
351
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#noRandomize() noRandomize}.
352
     */
353
    public static final String PROP_NORANDOMIZE = PFX + "norandomize";
354
    /**
355
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#noResolveHostnames() noResolveHostnames}.
356
     */
357
    public static final String PROP_NO_RESOLVE_HOSTNAMES = PFX + "noResolveHostnames";
358
    /**
359
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#reportNoResponders() reportNoResponders}.
360
     */
361
    public static final String PROP_REPORT_NO_RESPONDERS = PFX + "reportNoResponders";
362
    /**
363
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#clientSideLimitChecks() clientSideLimitChecks}.
364
     */
365
    public static final String PROP_CLIENT_SIDE_LIMIT_CHECKS = PFX + "clientsidelimitchecks";
366
    /**
367
     * Property used to configure a builder from a Properties object. {@value},
368
     * see {@link Builder#servers(String[]) servers}. The value can be a comma-separated list of server URLs.
369
     */
370
    public static final String PROP_SERVERS = PFX + "servers";
371
    /**
372
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#userInfo(String, String)
373
     * userInfo}.
374
     */
375
    public static final String PROP_PASSWORD = PFX + "password";
376
    /**
377
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#userInfo(String, String)
378
     * userInfo}.
379
     */
380
    public static final String PROP_USERNAME = PFX + "username";
381
    /**
382
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#token(String) token}.
383
     */
384
    public static final String PROP_TOKEN = PFX + "token";
385
    /**
386
     * Property used to configure the token supplier from a Properties object. {@value}, see {@link Builder#tokenSupplier(Supplier) tokenSupplier}.
387
     */
388
    public static final String PROP_TOKEN_SUPPLIER = PFX + "token.supplier";
389
    /**
390
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#server(String) server}.
391
     */
392
    public static final String PROP_URL = PFX + "url";
393
    /**
394
     * Property used to configure a builder from a Properties object. {@value},
395
     *  see {@link Builder#sslContext(SSLContext) sslContext}.
396
     * This property is a boolean flag, but it tells the options parser to use the
397
     * default SSL context. Set the default context before creating the options.
398
     */
399
    public static final String PROP_SECURE = PFX + "secure";
400
    /**
401
     * Property used to configure a builder from a Properties object.
402
     * {@value}, see {@link Builder#sslContext(SSLContext) sslContext}.
403
     * This property is a boolean flag, but it tells the options parser to use
404
     * an SSL context that takes any server TLS certificate and does not provide
405
     * its own. The server must have tls_verify turned OFF for this option to work.
406
     */
407
    public static final String PROP_OPENTLS = PFX + "opentls";
408
    /**
409
     * Property used to configure a builder from a Properties object.
410
     * {@value}, see {@link Builder#maxMessagesInOutgoingQueue(int) maxMessagesInOutgoingQueue}.
411
     */
412
    public static final String PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE = PFX + "outgoingqueue.maxmessages";
413
    /**
414
     * Property used to configure a builder from a Properties object.
415
     * {@value}, see {@link Builder#discardMessagesWhenOutgoingQueueFull()
416
     * discardMessagesWhenOutgoingQueueFull}.
417
     */
418
    public static final String PROP_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL = PFX + "outgoingqueue.discardwhenfull";
419
    /**
420
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#oldRequestStyle()
421
     * oldRequestStyle}.
422
     */
423
    public static final String PROP_USE_OLD_REQUEST_STYLE = "use.old.request.style";
424
    /**
425
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#maxControlLine(int)
426
     * maxControlLine}.
427
     */
428
    public static final String PROP_MAX_CONTROL_LINE = "max.control.line";
429
    /**
430
     * Property used to set the inbox prefix
431
     */
432
    public static final String PROP_INBOX_PREFIX = "inbox.prefix";
433
    /**
434
     * Property used to set whether to ignore discovered servers when connecting
435
     */
436
    public static final String PROP_IGNORE_DISCOVERED_SERVERS = "ignore_discovered_servers";
437
    /**
438
     * Preferred property used to set whether to ignore discovered servers when connecting
439
     */
440
    public static final String PROP_IGNORE_DISCOVERED_SERVERS_PREFERRED = "ignore.discovered.servers";
441
    /**
442
     * Property used to set class name for ServerPool implementation
443
     * {@link Builder#serverPool(ServerPool) serverPool}.
444
     */
445
    public static final String PROP_SERVERS_POOL_IMPLEMENTATION_CLASS = "servers_pool_implementation_class";
446
    /**
447
     * Preferred property used to set class name for ServerPool implementation
448
     * {@link Builder#serverPool(ServerPool) serverPool}.
449
     */
450
    public static final String PROP_SERVERS_POOL_IMPLEMENTATION_CLASS_PREFERRED = "servers.pool.implementation.class";
451
    /**
452
     * Property used to set class name for the Dispatcher Factory
453
     * {@link Builder#dispatcherFactory(DispatcherFactory) dispatcherFactory}.
454
     */
455
    public static final String PROP_DISPATCHER_FACTORY_CLASS = "dispatcher.factory.class";
456
    /**
457
     * Property used to set class name for the SSLContextFactory
458
     * {@link Builder#sslContextFactory(SSLContextFactory) sslContextFactory}.
459
     */
460
    public static final String PROP_SSL_CONTEXT_FACTORY_CLASS = "ssl.context.factory.class";
461
    /**
462
     * Property for the keystore path used to create an SSLContext
463
     */
464
    public static final String PROP_KEYSTORE = PFX + "keyStore";
465
    /**
466
     * Property for the keystore password used to create an SSLContext
467
     */
468
    public static final String PROP_KEYSTORE_PASSWORD = PFX + "keyStorePassword";
469
    /**
470
     * Property for the truststore path used to create an SSLContext
471
     */
472
    public static final String PROP_TRUSTSTORE = PFX + "trustStore";
473
    /**
474
     * Property for the truststore password used to create an SSLContext
475
     */
476
    public static final String PROP_TRUSTSTORE_PASSWORD = PFX + "trustStorePassword";
477
    /**
478
     * Property for the algorithm used to create an SSLContext
479
     */
480
    public static final String PROP_TLS_ALGORITHM = PFX + "tls.algorithm";
481
    /**
482
     * Property used to set the path to a credentials file to be used in a FileAuthHandler
483
     */
484
    public static final String PROP_CREDENTIAL_PATH = PFX + "credential.path";
485
    /**
486
     * Property used to configure tls first behavior
487
     * This property is a boolean flag, telling connections whether
488
     * to do TLS upgrade first, before INFO
489
     */
490
    public static final String PROP_TLS_FIRST = PFX + "tls.first";
491
    /**
492
     * This property is used to enable support for UTF8 subjects. See {@link Builder#supportUTF8Subjects() supportUTF8Subjects()}
493
     */
494
    public static final String PROP_UTF8_SUBJECTS = "allow.utf8.subjects";
495
    /**
496
     * Property used to throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}.
497
     * {@link Builder#useTimeoutException()}.
498
     */
499
    public static final String PROP_USE_TIMEOUT_EXCEPTION = PFX + "use.timeout.exception";
500
    /**
501
     * Property used to a dispatcher that dispatches messages via the executor service instead of with a blocking call.
502
     * {@link Builder#useDispatcherWithExecutor()}.
503
     */
504
    public static final String PROP_USE_DISPATCHER_WITH_EXECUTOR = PFX + "use.dispatcher.with.executor";
505
    /**
506
     * Property used to configure a builder from a Properties object. {@value}, see {@link Builder#forceFlushOnRequest() forceFlushOnRequest}.
507
     */
508
    public static final String PROP_FORCE_FLUSH_ON_REQUEST = PFX + "force.flush.on.request";
509
    /**
510
     * Property used to set class name for the Executor Service (executor) class
511
     * {@link Builder#executor(ExecutorService) executor}.
512
     */
513
    public static final String PROP_EXECUTOR_SERVICE_CLASS = "executor.service.class";
514
    /**
515
     * Property used to set class name for the Executor Service (executor) class
516
     * {@link Builder#executor(ExecutorService) executor}.
517
     */
518
    public static final String PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS = "scheduled.executor.service.class";
519
    /**
520
     * Property used to set class name for the Connect Thread Factory
521
     * {@link Builder#connectThreadFactory(ThreadFactory) connectThreadFactory}.
522
     */
523
    public static final String PROP_CONNECT_THREAD_FACTORY_CLASS = "connect.thread.factory.class";
524
    /**
525
     * Property used to set class name for the Callback Thread Factory
526
     * {@link Builder#callbackThreadFactory(ThreadFactory) callbackThreadFactory}.
527
     */
528
    public static final String PROP_CALLBACK_THREAD_FACTORY_CLASS = "callback.thread.factory.class";
529
    /**
530
     * Property used to set class name for the ReaderListener implementation
531
     * {@link Builder#readListener(ReadListener) readListener}.
532
     */
533
    public static final String PROP_READ_LISTENER_CLASS = "read.listener.class";
534

535
    /**
536
     * Property used to enable fast fallback algorithm for socket connection.
537
     * {@link Builder#enableFastFallback() enableFastFallback}.
538
     */
539
    public static final String PROP_FAST_FALLBACK = PFX + "fast.fallback";
540

541
    // ----------------------------------------------------------------------------------------------------
542
    // PROTOCOL CONNECT OPTION CONSTANTS
543
    // ----------------------------------------------------------------------------------------------------
544
    /**
545
     * Protocol key {@value}, see {@link Builder#verbose() verbose}.
546
     */
547
    static final String OPTION_VERBOSE = "verbose";
548

549
    /**
550
     * Protocol key {@value}, see {@link Builder#pedantic()
551
     * pedantic}.
552
     */
553
    static final String OPTION_PEDANTIC = "pedantic";
554

555
    /**
556
     * Protocol key {@value}, see
557
     * {@link Builder#sslContext(SSLContext) sslContext}.
558
     */
559
    static final String OPTION_TLS_REQUIRED = "tls_required";
560

561
    /**
562
     * Protocol key {@value}, see {@link Builder#token(String)
563
     * token}.
564
     */
565
    static final String OPTION_AUTH_TOKEN = "auth_token";
566

567
    /**
568
     * Protocol key {@value}, see
569
     * {@link Builder#userInfo(String, String) userInfo}.
570
     */
571
    static final String OPTION_USER = "user";
572

573
    /**
574
     * Protocol key {@value}, see
575
     * {@link Builder#userInfo(String, String) userInfo}.
576
     */
577
    static final String OPTION_PASSWORD = "pass";
578

579
    /**
580
     * Protocol key {@value}, see {@link Builder#connectionName(String)
581
     * connectionName}.
582
     */
583
    static final String OPTION_NAME = "name";
584

585
    /**
586
     * Protocol key {@value}, will be set to "Java".
587
     */
588
    static final String OPTION_LANG = "lang";
589

590
    /**
591
     * Protocol key {@value}, will be set to
592
     * {@link Nats#CLIENT_VERSION CLIENT_VERSION}.
593
     */
594
    static final String OPTION_VERSION = "version";
595

596
    /**
597
     * Protocol key {@value}, will be set to 1.
598
     */
599
    static final String OPTION_PROTOCOL = "protocol";
600

601
    /**
602
     * Echo key {@value}, determines if the server should echo to the client.
603
     */
604
    static final String OPTION_ECHO = "echo";
605

606
    /**
607
     * NKey key {@value}, the public key being used for sign-in.
608
     */
609
    static final String OPTION_NKEY = "nkey";
610

611
    /**
612
     * SIG key {@value}, the signature of the nonce sent by the server.
613
     */
614
    static final String OPTION_SIG = "sig";
615

616
    /**
617
     * JWT key {@value}, the user JWT to send to the server.
618
     */
619
    static final String OPTION_JWT = "jwt";
620

621
    /**
622
     * Headers key if headers are supported
623
     */
624
    static final String OPTION_HEADERS = "headers";
625

626
    /**
627
     * No Responders key if noresponders are supported
628
     */
629
    static final String OPTION_NORESPONDERS = "no_responders";
630

631
    // ----------------------------------------------------------------------------------------------------
632
    // CLASS VARIABLES
633
    // ----------------------------------------------------------------------------------------------------
634
    private final List<NatsUri> natsServerUris;
635
    private final List<String> unprocessedServers;
636
    private final boolean noRandomize;
637
    private final boolean noResolveHostnames;
638
    private final boolean reportNoResponders;
639
    private final String connectionName;
640
    private final boolean verbose;
641
    private final boolean pedantic;
642
    private final SSLContext sslContext;
643
    private final int maxReconnect;
644
    private final int maxControlLine;
645
    private final Duration reconnectWait;
646
    private final Duration reconnectJitter;
647
    private final Duration reconnectJitterTls;
648
    private final Duration connectionTimeout;
649
    private final int socketReadTimeoutMillis;
650
    private final Duration socketWriteTimeout;
651
    private final int socketSoLinger;
652
    private final Duration pingInterval;
653
    private final Duration requestCleanupInterval;
654
    private final int maxPingsOut;
655
    private final long reconnectBufferSize;
656
    private final char[] username;
657
    private final char[] password;
658
    private final Supplier<char[]> tokenSupplier;
659
    private final String inboxPrefix;
660
    private boolean useOldRequestStyle;
661
    private final int bufferSize;
662
    private final boolean noEcho;
663
    private final boolean noHeaders;
664
    private final boolean noNoResponders;
665
    private final boolean clientSideLimitChecks;
666
    private final boolean supportUTF8Subjects;
667
    private final int maxMessagesInOutgoingQueue;
668
    private final boolean discardMessagesWhenOutgoingQueueFull;
669
    private final boolean ignoreDiscoveredServers;
670
    private final boolean tlsFirst;
671
    private final boolean useTimeoutException;
672
    private final boolean useDispatcherWithExecutor;
673
    private final boolean forceFlushOnRequest;
674

675
    private final AuthHandler authHandler;
676
    private final ReconnectDelayHandler reconnectDelayHandler;
677

678
    private final ErrorListener errorListener;
679
    private final TimeTraceLogger timeTraceLogger;
680
    private final ConnectionListener connectionListener;
681
    private final ReadListener readListener;
682
    private final StatisticsCollector statisticsCollector;
683
    private final String dataPortType;
684

685
    private final boolean trackAdvancedStats;
686
    private final boolean traceConnection;
687

688
    private final ExecutorService executor;
689
    private final ScheduledExecutorService scheduledExecutor;
690
    private final ThreadFactory connectThreadFactory;
691
    private final ThreadFactory callbackThreadFactory;
692
    private final ServerPool serverPool;
693
    private final DispatcherFactory dispatcherFactory;
694

695
    private final List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
696
    private final Proxy proxy;
697
    private final boolean enableFastFallback;
698

699
    static class DefaultThreadFactory implements ThreadFactory {
700
        final String name;
701
        final AtomicInteger threadNo;
702

703
        public DefaultThreadFactory (String name){
1✔
704
            this.name = name;
1✔
705
            threadNo = new AtomicInteger(0);
1✔
706
        }
1✔
707

708
        public Thread newThread(@NonNull Runnable r) {
709
            String threadName = name+":"+threadNo.incrementAndGet();
1✔
710
            Thread t = new Thread(r,threadName);
1✔
711
            if (t.isDaemon()) {
1✔
712
                t.setDaemon(false);
×
713
            }
714
            if (t.getPriority() != Thread.NORM_PRIORITY) {
1✔
715
                t.setPriority(Thread.NORM_PRIORITY);
×
716
            }
717
            return t;
1✔
718
        }
719
    }
720

721
    static class DefaultTokenSupplier implements Supplier<char[]> {
722
        final char[] token;
723

724
        public DefaultTokenSupplier() {
1✔
725
            token = null;
1✔
726
        }
1✔
727

728
        public DefaultTokenSupplier(char[] token) {
1✔
729
            this.token = token == null || token.length == 0 ? null : token;
1✔
730
        }
1✔
731

732
        public DefaultTokenSupplier(String token) {
1✔
733
            token = Validator.emptyAsNull(token);
1✔
734
            this.token = token == null ? null : token.toCharArray();
1✔
735
        }
1✔
736

737
        @Override
738
        public char[] get() {
739
            return token;
1✔
740
        }
741
    }
742

743
    /**
744
     * Set old request style.
745
     * @param value true to use the old request style
746
     * @deprecated Use Builder
747
     */
748
    @Deprecated
749
    public void setOldRequestStyle(boolean value) {
750
        useOldRequestStyle = value;
1✔
751
    }
1✔
752

753
    // ----------------------------------------------------------------------------------------------------
754
    // BUILDER
755
    // ----------------------------------------------------------------------------------------------------
756
    /**
757
     * Creates a builder for the options in a fluent style
758
     * @return the builder.
759
     */
760
    public static Builder builder() {
761
        return new Builder();
1✔
762
    }
763

764
    /**
765
     * Options are created using a Builder. The builder supports chaining and will
766
     * create a default set of options if no methods are calls. The builder can also
767
     * be created from a properties object using the property names defined with the
768
     * prefix PROP_ in this class.
769
     * <p>A common usage for testing might be {@code new Options.Builder().server(myserverurl).noReconnect.build()}
770
     */
771
    public static class Builder {
772

773
        // ----------------------------------------------------------------------------------------------------
774
        // BUILDER VARIABLES
775
        // ----------------------------------------------------------------------------------------------------
776
        private final List<NatsUri> natsServerUris = new ArrayList<>();
1✔
777
        private final List<String> unprocessedServers = new ArrayList<>();
1✔
778
        private boolean noRandomize = false;
1✔
779
        private boolean noResolveHostnames = false;
1✔
780
        private boolean reportNoResponders = false;
1✔
781
        private String connectionName = null; // Useful for debugging -> "test: " + NatsTestServer.currentPort();
1✔
782
        private boolean verbose = false;
1✔
783
        private boolean pedantic = false;
1✔
784
        private SSLContext sslContext = null;
1✔
785
        private SSLContextFactory sslContextFactory = null;
1✔
786
        private int maxControlLine = DEFAULT_MAX_CONTROL_LINE;
1✔
787
        private int maxReconnect = DEFAULT_MAX_RECONNECT;
1✔
788
        private Duration reconnectWait = DEFAULT_RECONNECT_WAIT;
1✔
789
        private Duration reconnectJitter = DEFAULT_RECONNECT_JITTER;
1✔
790
        private Duration reconnectJitterTls = DEFAULT_RECONNECT_JITTER_TLS;
1✔
791
        private Duration connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
1✔
792
        private int socketReadTimeoutMillis = 0;
1✔
793
        private Duration socketWriteTimeout = DEFAULT_SOCKET_WRITE_TIMEOUT;
1✔
794
        private int socketSoLinger = -1;
1✔
795
        private Duration pingInterval = DEFAULT_PING_INTERVAL;
1✔
796
        private Duration requestCleanupInterval = DEFAULT_REQUEST_CLEANUP_INTERVAL;
1✔
797
        private int maxPingsOut = DEFAULT_MAX_PINGS_OUT;
1✔
798
        private long reconnectBufferSize = DEFAULT_RECONNECT_BUF_SIZE;
1✔
799
        private char[] username = null;
1✔
800
        private char[] password = null;
1✔
801
        private Supplier<char[]> tokenSupplier = new DefaultTokenSupplier();
1✔
802
        private boolean useOldRequestStyle = false;
1✔
803
        private int bufferSize = DEFAULT_BUFFER_SIZE;
1✔
804
        private boolean trackAdvancedStats = false;
1✔
805
        private boolean traceConnection = false;
1✔
806
        private boolean noEcho = false;
1✔
807
        private boolean noHeaders = false;
1✔
808
        private boolean noNoResponders = false;
1✔
809
        private boolean clientSideLimitChecks = true;
1✔
810
        private boolean supportUTF8Subjects = false;
1✔
811
        private String inboxPrefix = DEFAULT_INBOX_PREFIX;
1✔
812
        private int maxMessagesInOutgoingQueue = DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE;
1✔
813
        private boolean discardMessagesWhenOutgoingQueueFull = DEFAULT_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL;
1✔
814
        private boolean ignoreDiscoveredServers = false;
1✔
815
        private boolean tlsFirst = false;
1✔
816
        private boolean useTimeoutException = false;
1✔
817
        private boolean useDispatcherWithExecutor = false;
1✔
818
        private boolean forceFlushOnRequest = true; // true since it's the original b/w compatible way
1✔
819
        private ServerPool serverPool = null;
1✔
820
        private DispatcherFactory dispatcherFactory = null;
1✔
821

822
        private AuthHandler authHandler;
823
        private ReconnectDelayHandler reconnectDelayHandler;
824

825
        private ErrorListener errorListener = null;
1✔
826
        private TimeTraceLogger timeTraceLogger = null;
1✔
827
        private ConnectionListener connectionListener = null;
1✔
828
        private ReadListener readListener = null;
1✔
829
        private StatisticsCollector statisticsCollector = null;
1✔
830
        private String dataPortType = DEFAULT_DATA_PORT_TYPE;
1✔
831
        private ExecutorService executor;
832
        private ScheduledExecutorService scheduledExecutor;
833
        private ThreadFactory connectThreadFactory;
834
        private ThreadFactory callbackThreadFactory;
835
        private List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
836
        private Proxy proxy;
837

838
        private boolean useDefaultTls;
839
        private boolean useTrustAllTls;
840
        private String keystore;
841
        private char[] keystorePassword;
842
        private String truststore;
843
        private char[] truststorePassword;
844
        private String tlsAlgorithm = DEFAULT_TLS_ALGORITHM;
1✔
845
        private String credentialPath;
846
        private boolean enableFastFallback = false;
1✔
847

848
        /**
849
         * Constructs a new Builder with the default values.
850
         * <p>When {@link #build() build()} is called on a default builder it will add the {@link Options#DEFAULT_URL
851
         * default url} to its list of servers if there were no servers defined.</p>
852
         */
853
        public Builder() {}
1✔
854

855
        // ----------------------------------------------------------------------------------------------------
856
        // BUILD CONSTRUCTOR PROPS
857
        // ----------------------------------------------------------------------------------------------------
858
        /**
859
         * Constructs a new {@code Builder} from a {@link Properties} object.
860
         * <p>Methods called on the builder after construction can override the properties.</p>
861
         * @param props the {@link Properties} object
862
         */
863
        public Builder(Properties props) throws IllegalArgumentException {
1✔
864
            properties(props);
1✔
865
        }
1✔
866

867
        /**
868
         * Constructs a new {@code Builder} from a file that contains properties.
869
         * @param propertiesFilePath a resolvable path to a file from the location the application is running, either relative or absolute
870
         * @throws IOException if the properties file cannot be found, opened or read
871
         */
872
        public Builder(String propertiesFilePath) throws IOException {
1✔
873
            Properties props = new Properties();
1✔
874
            props.load(Files.newInputStream(Paths.get(propertiesFilePath)));
1✔
875
            properties(props);
1✔
876
        }
1✔
877

878
        // ----------------------------------------------------------------------------------------------------
879
        // BUILDER METHODS
880
        // ----------------------------------------------------------------------------------------------------
881

882
        /**
883
         * Add settings defined in the properties object
884
         * @param props the properties object
885
         * @throws IllegalArgumentException if the properties object is null
886
         * @return the Builder for chaining
887
         */
888
        public Builder properties(Properties props) {
889
            if (props == null) {
1✔
890
                throw new IllegalArgumentException("Properties cannot be null");
1✔
891
            }
892
            stringProperty(props, PROP_URL, this::server);
1✔
893
            stringProperty(props, PROP_SERVERS, str -> {
1✔
894
                String[] servers = str.trim().split(",\\s*");
1✔
895
                this.servers(servers);
1✔
896
            });
1✔
897

898
            charArrayProperty(props, PROP_USERNAME, ca -> this.username = ca);
1✔
899
            charArrayProperty(props, PROP_PASSWORD, ca -> this.password = ca);
1✔
900
            charArrayProperty(props, PROP_TOKEN, ca -> this.tokenSupplier = new DefaultTokenSupplier(ca));
1✔
901
            //noinspection unchecked
902
            classnameProperty(props, PROP_TOKEN_SUPPLIER, o -> this.tokenSupplier = (Supplier<char[]>) o);
1✔
903

904
            booleanProperty(props, PROP_SECURE, b -> this.useDefaultTls = b);
1✔
905
            booleanProperty(props, PROP_OPENTLS, b -> this.useTrustAllTls = b);
1✔
906

907
            classnameProperty(props, PROP_SSL_CONTEXT_FACTORY_CLASS, o -> this.sslContextFactory = (SSLContextFactory) o);
1✔
908
            stringProperty(props, PROP_KEYSTORE, s -> this.keystore = s);
1✔
909
            charArrayProperty(props, PROP_KEYSTORE_PASSWORD, ca -> this.keystorePassword = ca);
1✔
910
            stringProperty(props, PROP_TRUSTSTORE, s -> this.truststore = s);
1✔
911
            charArrayProperty(props, PROP_TRUSTSTORE_PASSWORD, ca -> this.truststorePassword = ca);
1✔
912
            stringProperty(props, PROP_TLS_ALGORITHM, s -> this.tlsAlgorithm = s);
1✔
913

914
            stringProperty(props, PROP_CREDENTIAL_PATH, s -> this.credentialPath = s);
1✔
915

916
            stringProperty(props, PROP_CONNECTION_NAME, s -> this.connectionName = s);
1✔
917

918
            booleanProperty(props, PROP_NORANDOMIZE, b -> this.noRandomize = b);
1✔
919
            booleanProperty(props, PROP_NO_RESOLVE_HOSTNAMES, b -> this.noResolveHostnames = b);
1✔
920
            booleanProperty(props, PROP_REPORT_NO_RESPONDERS, b -> this.reportNoResponders = b);
1✔
921

922
            stringProperty(props, PROP_CONNECTION_NAME, s -> this.connectionName = s);
1✔
923
            booleanProperty(props, PROP_VERBOSE, b -> this.verbose = b);
1✔
924
            booleanProperty(props, PROP_NO_ECHO, b -> this.noEcho = b);
1✔
925
            booleanProperty(props, PROP_NO_HEADERS, b -> this.noHeaders = b);
1✔
926
            booleanProperty(props, PROP_NO_NORESPONDERS, b -> this.noNoResponders = b);
1✔
927
            booleanProperty(props, PROP_CLIENT_SIDE_LIMIT_CHECKS, b -> this.clientSideLimitChecks = b);
1✔
928
            booleanProperty(props, PROP_UTF8_SUBJECTS, b -> this.supportUTF8Subjects = b);
1✔
929
            booleanProperty(props, PROP_PEDANTIC, b -> this.pedantic = b);
1✔
930

931
            intProperty(props, PROP_MAX_RECONNECT, DEFAULT_MAX_RECONNECT, i -> this.maxReconnect = i);
1✔
932
            durationProperty(props, PROP_RECONNECT_WAIT, DEFAULT_RECONNECT_WAIT, d -> this.reconnectWait = d);
1✔
933
            durationProperty(props, PROP_RECONNECT_JITTER, DEFAULT_RECONNECT_JITTER, d -> this.reconnectJitter = d);
1✔
934
            durationProperty(props, PROP_RECONNECT_JITTER_TLS, DEFAULT_RECONNECT_JITTER_TLS, d -> this.reconnectJitterTls = d);
1✔
935
            longProperty(props, PROP_RECONNECT_BUF_SIZE, DEFAULT_RECONNECT_BUF_SIZE, l -> this.reconnectBufferSize = l);
1✔
936
            durationProperty(props, PROP_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, d -> this.connectionTimeout = d);
1✔
937
            intProperty(props, PROP_SOCKET_READ_TIMEOUT_MS, -1, i -> this.socketReadTimeoutMillis = i);
1✔
938
            durationProperty(props, PROP_SOCKET_WRITE_TIMEOUT, DEFAULT_SOCKET_WRITE_TIMEOUT, d -> this.socketWriteTimeout = d);
1✔
939
            intProperty(props, PROP_SOCKET_SO_LINGER, -1, i -> socketSoLinger = i);
1✔
940

941
            intGtEqZeroProperty(props, PROP_MAX_CONTROL_LINE, DEFAULT_MAX_CONTROL_LINE, i -> this.maxControlLine = i);
1✔
942
            durationProperty(props, PROP_PING_INTERVAL, DEFAULT_PING_INTERVAL, d -> this.pingInterval = d);
1✔
943
            durationProperty(props, PROP_CLEANUP_INTERVAL, DEFAULT_REQUEST_CLEANUP_INTERVAL, d -> this.requestCleanupInterval = d);
1✔
944
            intProperty(props, PROP_MAX_PINGS, DEFAULT_MAX_PINGS_OUT, i -> this.maxPingsOut = i);
1✔
945
            booleanProperty(props, PROP_USE_OLD_REQUEST_STYLE, b -> this.useOldRequestStyle = b);
1✔
946

947
            classnameProperty(props, PROP_ERROR_LISTENER, o -> this.errorListener = (ErrorListener) o);
1✔
948
            classnameProperty(props, PROP_TIME_TRACE_LOGGER, o -> this.timeTraceLogger = (TimeTraceLogger) o);
1✔
949
            classnameProperty(props, PROP_CONNECTION_CB, o -> this.connectionListener = (ConnectionListener) o);
1✔
950
            classnameProperty(props, PROP_READ_LISTENER_CLASS, o -> this.readListener = (ReadListener) o);
1✔
951
            classnameProperty(props, PROP_STATISTICS_COLLECTOR, o -> this.statisticsCollector = (StatisticsCollector) o);
1✔
952

953
            stringProperty(props, PROP_DATA_PORT_TYPE, s -> this.dataPortType = s);
1✔
954
            stringProperty(props, PROP_INBOX_PREFIX, this::inboxPrefix);
1✔
955
            intGtEqZeroProperty(props, PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE, DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE, i -> this.maxMessagesInOutgoingQueue = i);
1✔
956
            booleanProperty(props, PROP_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL, b -> this.discardMessagesWhenOutgoingQueueFull = b);
1✔
957

958
            booleanProperty(props, PROP_IGNORE_DISCOVERED_SERVERS, b -> this.ignoreDiscoveredServers = b);
1✔
959
            booleanProperty(props, PROP_TLS_FIRST, b -> this.tlsFirst = b);
1✔
960
            booleanProperty(props, PROP_USE_TIMEOUT_EXCEPTION, b -> this.useTimeoutException = b);
1✔
961
            booleanProperty(props, PROP_USE_DISPATCHER_WITH_EXECUTOR, b -> this.useDispatcherWithExecutor = b);
1✔
962
            booleanProperty(props, PROP_FORCE_FLUSH_ON_REQUEST, b -> this.forceFlushOnRequest = b);
1✔
963
            booleanProperty(props, PROP_FAST_FALLBACK, b -> this.enableFastFallback = b);
1✔
964

965
            classnameProperty(props, PROP_SERVERS_POOL_IMPLEMENTATION_CLASS, o -> this.serverPool = (ServerPool) o);
1✔
966
            classnameProperty(props, PROP_DISPATCHER_FACTORY_CLASS, o -> this.dispatcherFactory = (DispatcherFactory) o);
1✔
967
            classnameProperty(props, PROP_EXECUTOR_SERVICE_CLASS, o -> this.executor = (ExecutorService) o);
1✔
968
            classnameProperty(props, PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS, o -> this.scheduledExecutor = (ScheduledExecutorService) o);
1✔
969
            classnameProperty(props, PROP_CONNECT_THREAD_FACTORY_CLASS, o -> this.connectThreadFactory = (ThreadFactory) o);
1✔
970
            classnameProperty(props, PROP_CALLBACK_THREAD_FACTORY_CLASS, o -> this.callbackThreadFactory = (ThreadFactory) o);
1✔
971
            return this;
1✔
972
        }
973

974
        /**
975
         * Add a server to the list of known servers.
976
         *
977
         * @param serverURL the URL for the server to add
978
         * @throws IllegalArgumentException if the url is not formatted correctly.
979
         * @return the Builder for chaining
980
         */
981
        public Builder server(String serverURL) {
982
            return servers(serverURL.trim().split(","));
1✔
983
        }
984

985
        /**
986
         * Add an array of servers to the list of known servers.
987
         *
988
         * @param servers A list of server URIs
989
         * @throws IllegalArgumentException if any url is not formatted correctly.
990
         * @return the Builder for chaining
991
         */
992
        public Builder servers(String[] servers) {
993
            for (String s : servers) {
1✔
994
                if (s != null && !s.isEmpty()) {
1✔
995
                    try {
996
                        String unprocessed = s.trim();
1✔
997
                        NatsUri nuri = new NatsUri(unprocessed);
1✔
998
                        if (!natsServerUris.contains(nuri)) {
1✔
999
                            natsServerUris.add(nuri);
1✔
1000
                            unprocessedServers.add(unprocessed);
1✔
1001
                        }
1002
                    }
1003
                    catch (URISyntaxException e) {
1✔
1004
                        throw new IllegalArgumentException(e);
1✔
1005
                    }
1✔
1006
                }
1007
            }
1008
            return this;
1✔
1009
        }
1010

1011
        /**
1012
         * Turn on the old request style that uses a new inbox and subscriber for each
1013
         * request.
1014
         * @return the Builder for chaining
1015
         */
1016
        public Builder oldRequestStyle() {
1017
            this.useOldRequestStyle = true;
1✔
1018
            return this;
1✔
1019
        }
1020

1021
        /**
1022
         * For the default server list provider, turn off server pool randomization.
1023
         * The default provider will pick servers from its list randomly on a reconnect.
1024
         * When noRandomize is set to true the default provider supplies a list that
1025
         * first contains servers as configured and then contains the servers as sent
1026
         * from the connected server.
1027
         * @return the Builder for chaining
1028
         */
1029
        public Builder noRandomize() {
1030
            this.noRandomize = true;
1✔
1031
            return this;
1✔
1032
        }
1033

1034
        /**
1035
         * For the default server list provider, whether to resolve hostnames when building server list.
1036
         * @return the Builder for chaining
1037
         */
1038
        public Builder noResolveHostnames() {
1039
            this.noResolveHostnames = true;
1✔
1040
            return this;
1✔
1041
        }
1042

1043
        public Builder reportNoResponders() {
1044
            this.reportNoResponders = true;
1✔
1045
            return this;
1✔
1046
        }
1047

1048
        /**
1049
         * Turn off echo. If supported by the nats-server version you are connecting to this
1050
         * flag will prevent the server from echoing messages back to the connection if it
1051
         * has subscriptions on the subject being published to.
1052
         * @return the Builder for chaining
1053
         */
1054
        public Builder noEcho() {
1055
            this.noEcho = true;
1✔
1056
            return this;
1✔
1057
        }
1058

1059
        /**
1060
         * Turn off header support. Some versions of the server don't support it.
1061
         * It's also not required if you don't use headers
1062
         * @return the Builder for chaining
1063
         */
1064
        public Builder noHeaders() {
1065
            this.noHeaders = true;
1✔
1066
            return this;
1✔
1067
        }
1068

1069
        /**
1070
         * Turn off noresponder support. Some versions of the server don't support it.
1071
         * @return the Builder for chaining
1072
         */
1073
        public Builder noNoResponders() {
1074
            this.noNoResponders = true;
1✔
1075
            return this;
1✔
1076
        }
1077

1078
        /**
1079
         * Set client side limit checks. Default is true
1080
         * @param checks the checks flag
1081
         * @return the Builder for chaining
1082
         */
1083
        public Builder clientSideLimitChecks(boolean checks) {
1084
            this.clientSideLimitChecks = checks;
1✔
1085
            return this;
1✔
1086
        }
1087

1088
        /**
1089
         * The client protocol is not clear about the encoding for subject names. For
1090
         * performance reasons, the Java client defaults to ASCII. You can enable UTF8
1091
         * with this method. The server, written in go, treats byte to string as UTF8 by default
1092
         * and should allow UTF8 subjects, but make sure to test any clients when using them.
1093
         * @return the Builder for chaining
1094
         */
1095
        public Builder supportUTF8Subjects() {
1096
            this.supportUTF8Subjects = true;
1✔
1097
            return this;
1✔
1098
        }
1099

1100
        /**
1101
         * Set the connection's optional Name.
1102
         *
1103
         * @param name the connections new name.
1104
         * @return the Builder for chaining
1105
         */
1106
        public Builder connectionName(String name) {
1107
            this.connectionName = name;
1✔
1108
            return this;
1✔
1109
        }
1110

1111
        /**
1112
         * Set the connection's inbox prefix. All inboxes will start with this string.
1113
         *
1114
         * @param prefix prefix to use.
1115
         * @return the Builder for chaining
1116
         */
1117
        public Builder inboxPrefix(String prefix) {
1118
            this.inboxPrefix = prefix;
1✔
1119

1120
            if (!this.inboxPrefix.endsWith(".")) {
1✔
1121
                this.inboxPrefix = this.inboxPrefix + ".";
1✔
1122
            }
1123
            return this;
1✔
1124
        }
1125

1126
        /**
1127
         * Turn on verbose mode with the server.
1128
         * @return the Builder for chaining
1129
         */
1130
        public Builder verbose() {
1131
            this.verbose = true;
1✔
1132
            return this;
1✔
1133
        }
1134

1135
        /**
1136
         * Turn on pedantic mode for the server, in relation to this connection.
1137
         * @return the Builder for chaining
1138
         */
1139
        public Builder pedantic() {
1140
            this.pedantic = true;
1✔
1141
            return this;
1✔
1142
        }
1143

1144
        /**
1145
         * Turn on advanced stats, primarily for test/benchmarks. These are visible if you
1146
         * call toString on the {@link Statistics Statistics} object.
1147
         * @return the Builder for chaining
1148
         */
1149
        public Builder turnOnAdvancedStats() {
1150
            this.trackAdvancedStats = true;
1✔
1151
            return this;
1✔
1152
        }
1153

1154
        /**
1155
         * Enable connection trace messages. Messages are printed to standard out. This option is for very
1156
         * fine-grained debugging of connection issues.
1157
         * @return the Builder for chaining
1158
         */
1159
        public Builder traceConnection() {
1160
            this.traceConnection = true;
1✔
1161
            return this;
1✔
1162
        }
1163

1164
        /**
1165
         * Sets the options to use the default SSL Context, if it exists.
1166
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1167
         * @return the Builder for chaining
1168
         */
1169
        public Builder secure() throws NoSuchAlgorithmException {
1170
            useDefaultTls = true;
1✔
1171
            return this;
1✔
1172
        }
1173

1174
        /**
1175
         * Set the options to use an SSL context that accepts any server certificate and has no client certificates.
1176
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1177
         * @return the Builder for chaining
1178
         */
1179
        public Builder opentls() throws NoSuchAlgorithmException {
1180
            useTrustAllTls = true;
1✔
1181
            return this;
1✔
1182
        }
1183

1184
        /**
1185
         * Set the SSL context, requires that the server supports TLS connections and
1186
         * the URI specifies TLS.
1187
         * If provided, the context takes precedence over any other TLS/SSL properties
1188
         * set in the builder, including the sslContextFactory
1189
         * @param ctx the SSL Context to use for TLS connections
1190
         * @return the Builder for chaining
1191
         */
1192
        public Builder sslContext(SSLContext ctx) {
1193
            this.sslContext = ctx;
1✔
1194
            return this;
1✔
1195
        }
1196

1197
        /**
1198
         * Set the factory that provides the ssl context. The factory is superseded
1199
         * by an instance of SSLContext
1200
         * @param sslContextFactory the SSL Context for use to create a ssl context
1201
         * @return the Builder for chaining
1202
         */
1203
        public Builder sslContextFactory(SSLContextFactory sslContextFactory) {
1204
            this.sslContextFactory = sslContextFactory;
1✔
1205
            return this;
1✔
1206
        }
1207

1208
        /**
1209
         *
1210
         * @param keystore the path to the keystore file
1211
         * @return the Builder for chaining
1212
         */
1213
        public Builder keystorePath(String keystore) {
1214
            this.keystore = emptyAsNull(keystore);
1✔
1215
            return this;
1✔
1216
        }
1217

1218
        /**
1219
         *
1220
         * @param keystorePassword the password for the keystore
1221
         * @return the Builder for chaining
1222
         */
1223
        public Builder keystorePassword(char[] keystorePassword) {
1224
            this.keystorePassword = keystorePassword == null || keystorePassword.length == 0 ? null : keystorePassword;
1✔
1225
            return this;
1✔
1226
        }
1227

1228
        /**
1229
         *
1230
         * @param truststore the path to the trust store file
1231
         * @return the Builder for chaining
1232
         */
1233
        public Builder truststorePath(String truststore) {
1234
            this.truststore = emptyAsNull(truststore);
1✔
1235
            return this;
1✔
1236
        }
1237

1238
        /**
1239
         *
1240
         * @param truststorePassword the password for the trust store
1241
         * @return the Builder for chaining
1242
         */
1243
        public Builder truststorePassword(char[] truststorePassword) {
1244
            this.truststorePassword = truststorePassword == null || truststorePassword.length == 0 ? null : truststorePassword;
1✔
1245
            return this;
1✔
1246
        }
1247

1248
        /**
1249
         *
1250
         * @param tlsAlgorithm the tls algorithm. Default is {@value SSLUtils#DEFAULT_TLS_ALGORITHM}
1251
         * @return the Builder for chaining
1252
         */
1253
        public Builder tlsAlgorithm(String tlsAlgorithm) {
1254
            this.tlsAlgorithm = emptyOrNullAs(tlsAlgorithm, DEFAULT_TLS_ALGORITHM);
1✔
1255
            return this;
1✔
1256
        }
1257

1258
        /**
1259
         *
1260
         * @param credentialPath the path to the credentials file for creating an {@link AuthHandler AuthHandler}
1261
         * @return the Builder for chaining
1262
         */
1263
        public Builder credentialPath(String credentialPath) {
1264
            this.credentialPath = emptyAsNull(credentialPath);
1✔
1265
            return this;
1✔
1266
        }
1267

1268
        /**
1269
         * Equivalent to calling maxReconnects with 0, {@link #maxReconnects(int) maxReconnects}.
1270
         * @return the Builder for chaining
1271
         */
1272
        public Builder noReconnect() {
1273
            this.maxReconnect = 0;
1✔
1274
            return this;
1✔
1275
        }
1276

1277
        /**
1278
         * Set the maximum number of reconnect attempts. Use 0 to turn off
1279
         * auto-reconnect. Use -1 to turn on infinite reconnects.
1280
         *
1281
         * <p>The reconnect count is incremented on a per-server basis, so if the server list contains 5 servers
1282
         * but max reconnects is set to 3, only 3 of those servers will be tried.</p>
1283
         *
1284
         * <p>This library has a slight difference from some NATS clients, if you set the maxReconnects to zero
1285
         * there will not be any reconnect attempts, regardless of the number of known servers.</p>
1286
         *
1287
         * <p>The reconnect state is entered when the connection is connected and loses
1288
         * that connection. During the initial connection attempt, the client will cycle over
1289
         * its server list one time, regardless of what maxReconnects is set to. The only exception
1290
         * to this is the async connect method {@link Nats#connectAsynchronously(Options, boolean) connectAsynchronously}.</p>
1291
         *
1292
         * @param max the maximum reconnect attempts
1293
         * @return the Builder for chaining
1294
         */
1295
        public Builder maxReconnects(int max) {
1296
            this.maxReconnect = max;
1✔
1297
            return this;
1✔
1298
        }
1299

1300
        /**
1301
         * Set the time to wait between reconnect attempts to the same server. This setting is only used
1302
         * by the client when the same server appears twice in the reconnect attempts, either because it is the
1303
         * only known server or by random chance. Note, the randomization of the server list doesn't occur per
1304
         * attempt, it is performed once at the start, so if there are 2 servers in the list you will never encounter
1305
         * the reconnect wait.
1306
         *
1307
         * @param time the time to wait
1308
         * @return the Builder for chaining
1309
         */
1310
        public Builder reconnectWait(Duration time) {
1311
            this.reconnectWait = time;
1✔
1312
            return this;
1✔
1313
        }
1314

1315
        /**
1316
         * Set the jitter time to wait between reconnect attempts to the same server. This setting is used to vary
1317
         * the reconnect wait to avoid multiple clients trying to reconnect to servers at the same time.
1318
         *
1319
         * @param time the time to wait
1320
         * @return the Builder for chaining
1321
         */
1322
        public Builder reconnectJitter(Duration time) {
1323
            this.reconnectJitter = time;
1✔
1324
            return this;
1✔
1325
        }
1326

1327
        /**
1328
         * Set the jitter time for a tls/secure connection to wait between reconnect attempts to the same server.
1329
         * This setting is used to vary the reconnect wait to avoid multiple clients trying to reconnect to
1330
         * servers at the same time.
1331
         *
1332
         * @param time the time to wait
1333
         * @return the Builder for chaining
1334
         */
1335
        public Builder reconnectJitterTls(Duration time) {
1336
            this.reconnectJitterTls = time;
1✔
1337
            return this;
1✔
1338
        }
1339

1340
        /**
1341
         * Set the maximum length of a control line sent by this connection. This value is also configured
1342
         * in the server but the protocol doesn't currently forward that setting. Configure it here so that
1343
         * the client can ensure that messages are valid before sending to the server.
1344
         *
1345
         * @param bytes the max byte count
1346
         * @return the Builder for chaining
1347
         */
1348
        public Builder maxControlLine(int bytes) {
1349
            this.maxControlLine = bytes < 0 ? DEFAULT_MAX_CONTROL_LINE : bytes;
1✔
1350
            return this;
1✔
1351
        }
1352

1353
        /**
1354
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1355
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1356
         *
1357
         * @param connectionTimeout the time to wait
1358
         * @return the Builder for chaining
1359
         */
1360
        public Builder connectionTimeout(Duration connectionTimeout) {
1361
            this.connectionTimeout = connectionTimeout;
1✔
1362
            return this;
1✔
1363
        }
1364

1365
        /**
1366
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1367
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1368
         *
1369
         * @param connectionTimeoutMillis the time to wait in milliseconds
1370
         * @return the Builder for chaining
1371
         */
1372
        public Builder connectionTimeout(long connectionTimeoutMillis) {
1373
            this.connectionTimeout = Duration.ofMillis(connectionTimeoutMillis);
×
1374
            return this;
×
1375
        }
1376

1377
        /**
1378
         * Set the timeout to use around socket reads
1379
         * @param socketReadTimeoutMillis the timeout milliseconds
1380
         * @return the Builder for chaining
1381
         */
1382
        public Builder socketReadTimeoutMillis(int socketReadTimeoutMillis) {
UNCOV
1383
            this.socketReadTimeoutMillis = socketReadTimeoutMillis;
×
UNCOV
1384
            return this;
×
1385
        }
1386

1387
        /**
1388
         * Set the timeout to use around socket writes
1389
         * @param socketWriteTimeoutMillis the timeout milliseconds
1390
         * @return the Builder for chaining
1391
         */
1392
        public Builder socketWriteTimeout(long socketWriteTimeoutMillis) {
1393
            socketWriteTimeout = Duration.ofMillis(socketWriteTimeoutMillis);
1✔
1394
            return this;
1✔
1395
        }
1396

1397
        /**
1398
         * Set the timeout to use around socket writes
1399
         * @param socketWriteTimeout the timeout duration
1400
         * @return the Builder for chaining
1401
         */
1402
        public Builder socketWriteTimeout(Duration socketWriteTimeout) {
1403
            this.socketWriteTimeout = socketWriteTimeout;
1✔
1404
            return this;
1✔
1405
        }
1406

1407
        /**
1408
         * Set the value of the socket SO LINGER property in seconds.
1409
         * This feature is used by library data port implementations.
1410
         * Setting this is a last resort if socket closes are a problem
1411
         * in your environment, otherwise it's generally not necessary
1412
         * to set this. The value must be greater than or equal to 0
1413
         * to have the code call socket.setSoLinger with true and the timeout value
1414
         * @param socketSoLinger the number of seconds to linger
1415
         * @return the Builder for chaining
1416
         */
1417
        public Builder socketSoLinger(int socketSoLinger) {
1418
            this.socketSoLinger = socketSoLinger;
×
1419
            return this;
×
1420
        }
1421

1422
        /**
1423
         * Set the interval between attempts to pings the server. These pings are automated,
1424
         * and capped by {@link #maxPingsOut(int) maxPingsOut()}. As of 2.4.4 the library
1425
         * may wait up to 2 * time to send a ping. Incoming traffic from the server can postpone
1426
         * the next ping to avoid pings taking up bandwidth during busy messaging.
1427
         * Keep in mind that a ping requires a round trip to the server. Setting this value to a small
1428
         * number can result in quick failures due to maxPingsOut being reached, these failures will
1429
         * force a disconnect/reconnect which can result in messages being held back or failed. In general,
1430
         * the ping interval should be set in seconds but this value is not enforced as it would result in
1431
         * an API change from the 2.0 release.
1432
         *
1433
         * @param time the time between client to server pings
1434
         * @return the Builder for chaining
1435
         */
1436
        public Builder pingInterval(Duration time) {
1437
            this.pingInterval = time == null ? DEFAULT_PING_INTERVAL : time;
1✔
1438
            return this;
1✔
1439
        }
1440

1441
        /**
1442
         * Set the interval between cleaning passes on outstanding request futures that are cancelled or timeout
1443
         * in the application code.
1444
         *
1445
         * <p>The default value is probably reasonable, but this interval is useful in a very noisy network
1446
         * situation where lots of requests are used.
1447
         *
1448
         * @param time the cleaning interval
1449
         * @return the Builder for chaining
1450
         */
1451
        public Builder requestCleanupInterval(Duration time) {
1452
            this.requestCleanupInterval = time;
1✔
1453
            return this;
1✔
1454
        }
1455

1456
        /**
1457
         * Set the maximum number of pings the client can have in flight.
1458
         *
1459
         * @param max the max pings
1460
         * @return the Builder for chaining
1461
         */
1462
        public Builder maxPingsOut(int max) {
1463
            this.maxPingsOut = max;
1✔
1464
            return this;
1✔
1465
        }
1466

1467
        /**
1468
         * Sets the initial size for buffers in the connection, primarily for testing.
1469
         * @param size the size in bytes to make buffers for connections created with this options
1470
         * @return the Builder for chaining
1471
         */
1472
        public Builder bufferSize(int size) {
1473
            this.bufferSize = size;
1✔
1474
            return this;
1✔
1475
        }
1476

1477
        /**
1478
         * Set the maximum number of bytes to buffer in the client when trying to
1479
         * reconnect. When this value is exceeded the client will start to drop messages.
1480
         * The count of dropped messages can be read from the {@link Statistics#getDroppedCount() Statistics}.
1481
         * A value of zero will disable the reconnect buffer, a value less than zero means unlimited. Caution
1482
         * should be used for negative numbers as they can result in an unreliable network connection plus a
1483
         * high message rate leading to an out of memory error.
1484
         *
1485
         * @param size the size in bytes
1486
         * @return the Builder for chaining
1487
         */
1488
        public Builder reconnectBufferSize(long size) {
1489
            this.reconnectBufferSize = size;
1✔
1490
            return this;
1✔
1491
        }
1492

1493
        /**
1494
         * Set the username and password for basic authentication.
1495
         * If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
1496
         * these values can be used as a fallback.
1497
         * use the char[] version instead for better security
1498
         *
1499
         * @param userName a non-empty userName
1500
         * @param password the password, in plain text
1501
         * @return the Builder for chaining
1502
         */
1503
        public Builder userInfo(String userName, String password) {
1504
            this.username = userName.toCharArray();
1✔
1505
            this.password = password.toCharArray();
1✔
1506
            return this;
1✔
1507
        }
1508

1509
        /**
1510
         * Set the username and password for basic authentication.
1511
         * If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
1512
         * these values can be used as a fallback.
1513
         *
1514
         * @param userName a non-empty userName
1515
         * @param password the password, in plain text
1516
         * @return the Builder for chaining
1517
         */
1518
        public Builder userInfo(char[] userName, char[] password) {
1519
            this.username = userName;
1✔
1520
            this.password = password;
1✔
1521
            return this;
1✔
1522
        }
1523

1524
        /**
1525
         * Set the token for token-based authentication.
1526
         * If a token is provided in a server URI, it overrides this value.
1527
         *
1528
         * @param token The token
1529
         * @return the Builder for chaining
1530
         * @deprecated use the char[] version instead for better security
1531
         */
1532
        @Deprecated
1533
        public Builder token(String token) {
1534
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1535
            return this;
1✔
1536
        }
1537

1538
        /**
1539
         * Set the token for token-based authentication.
1540
         * If a token is provided in a server URI, it overrides this value.
1541
         *
1542
         * @param token The token
1543
         * @return the Builder for chaining
1544
         */
1545
        public Builder token(char[] token) {
1546
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1547
            return this;
1✔
1548
        }
1549

1550
        /**
1551
         * Set the token supplier for token-based authentication.
1552
         * If a token is provided in a server URI, it overrides this value.
1553
         *
1554
         * @param tokenSupplier The tokenSupplier
1555
         * @return the Builder for chaining
1556
         */
1557
        public Builder tokenSupplier(Supplier<char[]> tokenSupplier) {
1558
            this.tokenSupplier = tokenSupplier == null ? new DefaultTokenSupplier() : tokenSupplier;
1✔
1559
            return this;
1✔
1560
        }
1561

1562
        /**
1563
         * Set the {@link AuthHandler AuthHandler} to sign the server nonce for authentication in
1564
         * nonce-mode.
1565
         *
1566
         * @param handler The new AuthHandler for this connection.
1567
         * @return the Builder for chaining
1568
         */
1569
        public Builder authHandler(AuthHandler handler) {
1570
            this.authHandler = handler;
1✔
1571
            return this;
1✔
1572
        }
1573

1574
        /**
1575
         * Set the {@link ReconnectDelayHandler ReconnectDelayHandler} for custom reconnect duration
1576
         *
1577
         * @param handler The new ReconnectDelayHandler for this connection.
1578
         * @return the Builder for chaining
1579
         */
1580
        public Builder reconnectDelayHandler(ReconnectDelayHandler handler) {
1581
            this.reconnectDelayHandler = handler;
1✔
1582
            return this;
1✔
1583
        }
1584

1585
        /**
1586
         * Set the {@link ErrorListener ErrorListener} to receive asynchronous error events related to this
1587
         * connection.
1588
         *
1589
         * @param listener The new ErrorListener for this connection.
1590
         * @return the Builder for chaining
1591
         */
1592
        public Builder errorListener(ErrorListener listener) {
1593
            this.errorListener = listener;
1✔
1594
            return this;
1✔
1595
        }
1596

1597
        /**
1598
         * Set the {@link TimeTraceLogger TimeTraceLogger} to receive trace events related to this connection.
1599
         * @param logger The new TimeTraceLogger for this connection.
1600
         * @return the Builder for chaining
1601
         */
1602
        public Builder timeTraceLogger(TimeTraceLogger logger) {
1603
            this.timeTraceLogger = logger;
1✔
1604
            return this;
1✔
1605
        }
1606

1607
        /**
1608
         * Set the {@link ConnectionListener ConnectionListener} to receive asynchronous notifications of disconnect
1609
         * events.
1610
         *
1611
         * @param listener The new ConnectionListener for this type of event.
1612
         * @return the Builder for chaining
1613
         */
1614
        public Builder connectionListener(ConnectionListener listener) {
1615
            this.connectionListener = listener;
1✔
1616
            return this;
1✔
1617
        }
1618

1619
        /**
1620
         * Sets a listener to be notified on incoming protocol/message
1621
         *
1622
         * @param readListener the listener
1623
         * @return the Builder for chaining
1624
         */
1625
        public Builder readListener(ReadListener readListener) {
1626
            this.readListener = readListener;
×
1627
            return this;
×
1628
        }
1629

1630
        /**
1631
         * Set the {@link StatisticsCollector StatisticsCollector} to collect connection metrics.
1632
         * <p>
1633
         * If not set, then a default implementation will be used.
1634
         *
1635
         * @param collector the new StatisticsCollector for this connection.
1636
         * @return the Builder for chaining
1637
         */
1638
        public Builder statisticsCollector(StatisticsCollector collector) {
1639
            this.statisticsCollector = collector;
×
1640
            return this;
×
1641
        }
1642

1643
        /**
1644
         * Set the {@link ExecutorService ExecutorService} used to run threaded tasks. The default is a
1645
         * cached thread pool that names threads after the connection name (or a default). This executor
1646
         * is used for reading and writing the underlying sockets as well as for each Dispatcher.
1647
         * The default executor uses a short keepalive time, 500ms, to insure quick shutdowns. This is reasonable
1648
         * since most threads from the executor are long-lived. If you customize, be sure to keep the shutdown
1649
         * effect in mind, executors can block for their keepalive time. The default executor also marks threads
1650
         * with priority normal and as non-daemon.
1651
         *
1652
         * @param executor The ExecutorService to use for connections built with these options.
1653
         * @return the Builder for chaining
1654
         */
1655
        public Builder executor(ExecutorService executor) {
1656
            this.executor = executor;
1✔
1657
            return this;
1✔
1658
        }
1659

1660
        /**
1661
         * Set the {@link ScheduledExecutorService ScheduledExecutorService} used to run scheduled task like
1662
         * heartbeat timers
1663
         * The default is a ScheduledThreadPoolExecutor that does not
1664
         *  execute delayed tasks after shutdown and removes tasks on cancel;
1665
         * @param scheduledExecutor The ScheduledExecutorService to use for timer tasks
1666
         * @return the Builder for chaining
1667
         */
1668
        public Builder scheduledExecutor(ScheduledExecutorService scheduledExecutor) {
1669
            this.scheduledExecutor = scheduledExecutor;
1✔
1670
            return this;
1✔
1671
        }
1672

1673
        /**
1674
         * Sets custom thread factory for the executor service
1675
         *
1676
         * @param threadFactory the thread factory to use for the executor service
1677
         * @return the Builder for chaining
1678
         */
1679
        public Builder connectThreadFactory(ThreadFactory threadFactory) {
1680
            this.connectThreadFactory = threadFactory;
1✔
1681
            return this;
1✔
1682
        }
1683

1684
        /**
1685
         * Sets custom thread factory for the executor service
1686
         *
1687
         * @param threadFactory the thread factory to use for the executor service
1688
         * @return the Builder for chaining
1689
         */
1690
        public Builder callbackThreadFactory(ThreadFactory threadFactory) {
1691
            this.callbackThreadFactory = threadFactory;
1✔
1692
            return this;
1✔
1693
        }
1694

1695
        /**
1696
         * Add an HttpRequest interceptor which can be used to modify the HTTP request when using websockets
1697
         *
1698
         * @param interceptor The interceptor
1699
         * @return the Builder for chaining
1700
         */
1701
        public Builder httpRequestInterceptor(java.util.function.Consumer<HttpRequest> interceptor) {
1702
            if (null == this.httpRequestInterceptors) {
1✔
1703
                this.httpRequestInterceptors = new ArrayList<>();
1✔
1704
            }
1705
            this.httpRequestInterceptors.add(interceptor);
1✔
1706
            return this;
1✔
1707
        }
1708

1709
        /**
1710
         * Overwrite the list of HttpRequest interceptors which can be used to modify the HTTP request when using websockets
1711
         *
1712
         * @param interceptors The list of interceptors
1713
         * @return the Builder for chaining
1714
         */
1715
        public Builder httpRequestInterceptors(Collection<? extends java.util.function.Consumer<HttpRequest>> interceptors) {
1716
            this.httpRequestInterceptors = new ArrayList<>(interceptors);
1✔
1717
            return this;
1✔
1718
        }
1719

1720
        /**
1721
         * Define a proxy to use when connecting.
1722
         *
1723
         * @param proxy is the HTTP or socks proxy to use.
1724
         * @return the Builder for chaining
1725
         */
1726
        public Builder proxy(Proxy proxy) {
1727
            this.proxy = proxy;
1✔
1728
            return this;
1✔
1729
        }
1730

1731
        /**
1732
         * The class to use for this connections data port. This is an advanced setting
1733
         * and primarily useful for testing.
1734
         *
1735
         * @param dataPortClassName a valid and accessible class name
1736
         * @return the Builder for chaining
1737
         */
1738
        public Builder dataPortType(String dataPortClassName) {
1739
            this.dataPortType = dataPortClassName == null ? DEFAULT_DATA_PORT_TYPE : dataPortClassName;
1✔
1740
            return this;
1✔
1741
        }
1742

1743
        /**
1744
         * Set the maximum number of messages in the outgoing queue.
1745
         *
1746
         * @param maxMessagesInOutgoingQueue the maximum number of messages in the outgoing queue
1747
         * @return the Builder for chaining
1748
         */
1749
        public Builder maxMessagesInOutgoingQueue(int maxMessagesInOutgoingQueue) {
1750
            this.maxMessagesInOutgoingQueue = maxMessagesInOutgoingQueue < 0
1✔
1751
                ? DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE
1752
                : maxMessagesInOutgoingQueue;
1753
            return this;
1✔
1754
        }
1755

1756
        /**
1757
         * Enable discard messages when the outgoing queue full. See {@link Builder#maxMessagesInOutgoingQueue(int) maxMessagesInOutgoingQueue}
1758
         *
1759
         * @return the Builder for chaining
1760
         */
1761
        public Builder discardMessagesWhenOutgoingQueueFull() {
1762
            this.discardMessagesWhenOutgoingQueueFull = true;
1✔
1763
            return this;
1✔
1764
        }
1765

1766
        /**
1767
         * Turn off use of discovered servers when connecting / reconnecting. Used in the default server list provider.
1768
         * @return the Builder for chaining
1769
         */
1770
        public Builder ignoreDiscoveredServers() {
1771
            this.ignoreDiscoveredServers = true;
1✔
1772
            return this;
1✔
1773
        }
1774

1775
        /**
1776
         * Set TLS Handshake First behavior on. Default is off.
1777
         * TLS Handshake First is used to instruct the library perform
1778
         * the TLS handshake right after the connect and before receiving
1779
         * the INFO protocol from the server. If this option is enabled
1780
         * but the server is not configured to perform the TLS handshake
1781
         * first, the connection will fail.
1782
         * @return the Builder for chaining
1783
         */
1784
        public Builder tlsFirst() {
1785
            this.tlsFirst = true;
1✔
1786
            return this;
1✔
1787
        }
1788

1789
        /**
1790
         * Throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}?
1791
         * @return the Builder for chaining
1792
         */
1793
        public Builder useTimeoutException() {
1794
            this.useTimeoutException = true;
×
1795
            return this;
×
1796
        }
1797

1798
        /**
1799
         * Instruct dispatchers to dispatch all messages as a task, instead of directly from dispatcher thread
1800
         * @return the Builder for chaining
1801
         */
1802
        public Builder useDispatcherWithExecutor() {
1803
            this.useDispatcherWithExecutor = true;
1✔
1804
            return this;
1✔
1805
        }
1806

1807
        /**
1808
         * Instruct requests to turn off flush on requests.
1809
         * @return the Builder for chaining
1810
         */
1811
        public Builder dontForceFlushOnRequest() {
1812
            this.forceFlushOnRequest = false;
×
1813
            return this;
×
1814
        }
1815

1816
        /**
1817
         * Set the ServerPool implementation for connections to use instead of the default implementation
1818
         * @param serverPool the implementation
1819
         * @return the Builder for chaining
1820
         */
1821
        public Builder serverPool(ServerPool serverPool) {
1822
            this.serverPool = serverPool;
1✔
1823
            return this;
1✔
1824
        }
1825

1826
        /**
1827
         * Set the DispatcherFactory implementation for connections to use instead of the default implementation
1828
         * @param dispatcherFactory the implementation
1829
         * @return the Builder for chaining
1830
         */
1831
        public Builder dispatcherFactory(DispatcherFactory dispatcherFactory) {
1832
            this.dispatcherFactory = dispatcherFactory;
×
1833
            return this;
×
1834
        }
1835

1836
        /**
1837
         * Whether to enable Fast fallback algorithm for socket connect
1838
         * @return the Builder for chaining
1839
         */
1840
        public Builder enableFastFallback() {
1841
            this.enableFastFallback = true;
1✔
1842
            return this;
1✔
1843
        }
1844

1845
        /**
1846
         * Build an Options object from this Builder.
1847
         *
1848
         * <p>If the Options builder was not provided with a server, a default one will be included
1849
         * {@link Options#DEFAULT_URL}. If only a single server URI is included, the builder
1850
         * will try a few things to make connecting easier:
1851
         * <ul>
1852
         * <li>If there is no user/password is set but the URI has them, {@code nats://user:password@server:port}, they will be used.
1853
         * <li>If there is no token is set but the URI has one, {@code nats://token@server:port}, it will be used.
1854
         * <li>If the URI is of the form tls:// and no SSL context was assigned, one is created, see {@link Options.Builder#secure() secure()}.
1855
         * <li>If the URI is of the form opentls:// and no SSL context was assigned one will be created
1856
         * that does not check the servers certificate for validity. This is not secure and only provided
1857
         * for tests and development.
1858
         * </ul>
1859
         *
1860
         * @return the new options object
1861
         * @throws IllegalStateException if there is a conflict in the options, like a token and a user/pass
1862
         */
1863
        public Options build() throws IllegalStateException {
1864
            // ----------------------------------------------------------------------------------------------------
1865
            // BUILD IMPL
1866
            // ----------------------------------------------------------------------------------------------------
1867
            if (this.username != null && tokenSupplier.get() != null) {
1✔
1868
                throw new IllegalStateException("Options can't have token and username");
1✔
1869
            }
1870

1871
            if (inboxPrefix == null) {
1✔
1872
                inboxPrefix = DEFAULT_INBOX_PREFIX;
×
1873
            }
1874

1875
            boolean checkUrisForSecure = true;
1✔
1876
            if (natsServerUris.isEmpty()) {
1✔
1877
                server(DEFAULT_URL);
1✔
1878
                checkUrisForSecure = false;
1✔
1879
            }
1880

1881
            // ssl context can be directly provided, but if it's not
1882
            // there might be a factory, or just see if we should make it ourselves
1883
            if (sslContext == null) {
1✔
1884
                if (sslContextFactory != null) {
1✔
1885
                    sslContext = sslContextFactory.createSSLContext(new SSLContextFactoryProperties.Builder()
1✔
1886
                        .keystore(keystore)
1✔
1887
                        .keystorePassword(keystorePassword)
1✔
1888
                        .truststore(truststore)
1✔
1889
                        .truststorePassword(truststorePassword)
1✔
1890
                        .tlsAlgorithm(tlsAlgorithm)
1✔
1891
                        .build());
1✔
1892
                }
1893
                else {
1894
                    if (keystore != null || truststore != null) {
1✔
1895
                        // the user provided keystore/truststore properties, the want us to make the sslContext that way
1896
                        try {
1897
                            sslContext = SSLUtils.createSSLContext(keystore, keystorePassword, truststore, truststorePassword, tlsAlgorithm);
1✔
1898
                        }
1899
                        catch (Exception e) {
×
1900
                            throw new IllegalStateException("Unable to create SSL context", e);
×
1901
                        }
1✔
1902
                    }
1903
                    else {
1904
                        // the sslContext has not been requested via factory or keystore/truststore properties
1905
                        // If we haven't been told to use the default or the trust all context
1906
                        // and the server isn't the default url, check to see if the server uris
1907
                        // suggest we need the ssl context.
1908
                        if (!useDefaultTls && !useTrustAllTls && checkUrisForSecure) {
1✔
1909
                            for (int i = 0; sslContext == null && i < natsServerUris.size(); i++) {
1✔
1910
                                NatsUri natsUri = natsServerUris.get(i);
1✔
1911
                                switch (natsUri.getScheme()) {
1✔
1912
                                    case TLS_PROTOCOL:
1913
                                    case SECURE_WEBSOCKET_PROTOCOL:
1914
                                        useDefaultTls = true;
1✔
1915
                                        break;
1✔
1916
                                    case OPENTLS_PROTOCOL:
1917
                                        useTrustAllTls = true;
1✔
1918
                                        break;
1919
                                }
1920
                            }
1921
                        }
1922

1923
                        // check trust all (open) first, in case they provided both
1924
                        // PROP_SECURE (secure) and PROP_OPENTLS (opentls)
1925
                        if (useTrustAllTls) {
1✔
1926
                            try {
1927
                                this.sslContext = SSLUtils.createTrustAllTlsContext();
1✔
1928
                            }
1929
                            catch (GeneralSecurityException e) {
×
1930
                                throw new IllegalStateException("Unable to create SSL context", e);
×
1931
                            }
1✔
1932
                        }
1933
                        else if (useDefaultTls) {
1✔
1934
                            try {
1935
                                this.sslContext = SSLContext.getDefault();
1✔
1936
                            }
1937
                            catch (NoSuchAlgorithmException e) {
×
1938
                                throw new IllegalStateException("Unable to create default SSL context", e);
×
1939
                            }
1✔
1940
                        }
1941
                    }
1942
                }
1943
            }
1944

1945
            if (tlsFirst && sslContext == null) {
1✔
1946
                throw new IllegalStateException("SSL context required for tls handshake first");
×
1947
            }
1948

1949
            if (credentialPath != null) {
1✔
1950
                File file = new File(credentialPath).getAbsoluteFile();
1✔
1951
                authHandler = Nats.credentials(file.toString());
1✔
1952
            }
1953

1954
            if (socketReadTimeoutMillis < 1) {
1✔
1955
                socketReadTimeoutMillis = 0; // just for consistency. The connection compares to gt 0
1✔
1956
            }
1957

1958
            if (socketWriteTimeout != null && socketWriteTimeout.toMillis() < 1) {
1✔
1959
                socketWriteTimeout = null;
×
1960
            }
1961

1962
            if (socketSoLinger < 0) {
1✔
1963
                socketSoLinger = -1;
1✔
1964
            }
1965

1966
            if (errorListener == null) {
1✔
1967
                errorListener = new ErrorListenerLoggerImpl();
1✔
1968
            }
1969

1970
            if (timeTraceLogger == null) {
1✔
1971
                if (traceConnection) {
1✔
1972
                    timeTraceLogger = (format, args) -> {
1✔
1973
                        String timeStr = DateTimeFormatter.ISO_TIME.format(LocalDateTime.now());
1✔
1974
                        System.out.println("[" + timeStr + "] connect trace: " + String.format(format, args));
1✔
1975
                    };
1✔
1976
                }
1977
                else {
1978
                    timeTraceLogger = (f, a) -> {};
1✔
1979
                }
1980
            }
1981
            else {
1982
                // if the dev provided an impl, we assume they meant to time trace the connection
1983
                traceConnection = true;
1✔
1984
            }
1985

1986
            return new Options(this);
1✔
1987
        }
1988

1989
        // ----------------------------------------------------------------------------------------------------
1990
        // BUILDER COPY CONSTRUCTOR
1991
        // ----------------------------------------------------------------------------------------------------
1992
        public Builder(Options o) {
1✔
1993
            if (o == null) {
1✔
1994
                throw new IllegalArgumentException("Options cannot be null");
1✔
1995
            }
1996

1997
            this.natsServerUris.addAll(o.natsServerUris);
1✔
1998
            this.unprocessedServers.addAll(o.unprocessedServers);
1✔
1999
            this.noRandomize = o.noRandomize;
1✔
2000
            this.noResolveHostnames = o.noResolveHostnames;
1✔
2001
            this.reportNoResponders = o.reportNoResponders;
1✔
2002
            this.connectionName = o.connectionName;
1✔
2003
            this.verbose = o.verbose;
1✔
2004
            this.pedantic = o.pedantic;
1✔
2005
            this.sslContext = o.sslContext;
1✔
2006
            this.maxReconnect = o.maxReconnect;
1✔
2007
            this.reconnectWait = o.reconnectWait;
1✔
2008
            this.reconnectJitter = o.reconnectJitter;
1✔
2009
            this.reconnectJitterTls = o.reconnectJitterTls;
1✔
2010
            this.connectionTimeout = o.connectionTimeout;
1✔
2011
            this.socketReadTimeoutMillis = o.socketReadTimeoutMillis;
1✔
2012
            this.socketWriteTimeout = o.socketWriteTimeout;
1✔
2013
            this.socketSoLinger = o.socketSoLinger;
1✔
2014
            this.pingInterval = o.pingInterval;
1✔
2015
            this.requestCleanupInterval = o.requestCleanupInterval;
1✔
2016
            this.maxPingsOut = o.maxPingsOut;
1✔
2017
            this.reconnectBufferSize = o.reconnectBufferSize;
1✔
2018
            this.username = o.username;
1✔
2019
            this.password = o.password;
1✔
2020
            this.tokenSupplier = o.tokenSupplier;
1✔
2021
            this.useOldRequestStyle = o.useOldRequestStyle;
1✔
2022
            this.maxControlLine = o.maxControlLine;
1✔
2023
            this.bufferSize = o.bufferSize;
1✔
2024
            this.noEcho = o.noEcho;
1✔
2025
            this.noHeaders = o.noHeaders;
1✔
2026
            this.noNoResponders = o.noNoResponders;
1✔
2027
            this.clientSideLimitChecks = o.clientSideLimitChecks;
1✔
2028
            this.supportUTF8Subjects = o.supportUTF8Subjects;
1✔
2029
            this.inboxPrefix = o.inboxPrefix;
1✔
2030
            this.traceConnection = o.traceConnection;
1✔
2031
            this.maxMessagesInOutgoingQueue = o.maxMessagesInOutgoingQueue;
1✔
2032
            this.discardMessagesWhenOutgoingQueueFull = o.discardMessagesWhenOutgoingQueueFull;
1✔
2033

2034
            this.authHandler = o.authHandler;
1✔
2035
            this.reconnectDelayHandler = o.reconnectDelayHandler;
1✔
2036

2037
            this.errorListener = o.errorListener;
1✔
2038
            this.timeTraceLogger = o.timeTraceLogger;
1✔
2039
            this.connectionListener = o.connectionListener;
1✔
2040
            this.readListener = o.readListener;
1✔
2041
            this.statisticsCollector = o.statisticsCollector;
1✔
2042
            this.dataPortType = o.dataPortType;
1✔
2043
            this.trackAdvancedStats = o.trackAdvancedStats;
1✔
2044
            this.executor = o.executor;
1✔
2045
            this.scheduledExecutor = o.scheduledExecutor;
1✔
2046
            this.callbackThreadFactory = o.callbackThreadFactory;
1✔
2047
            this.connectThreadFactory = o.connectThreadFactory;
1✔
2048
            this.httpRequestInterceptors = o.httpRequestInterceptors;
1✔
2049
            this.proxy = o.proxy;
1✔
2050

2051
            this.ignoreDiscoveredServers = o.ignoreDiscoveredServers;
1✔
2052
            this.tlsFirst = o.tlsFirst;
1✔
2053
            this.useTimeoutException = o.useTimeoutException;
1✔
2054
            this.useDispatcherWithExecutor = o.useDispatcherWithExecutor;
1✔
2055
            this.forceFlushOnRequest = o.forceFlushOnRequest;
1✔
2056

2057
            this.serverPool = o.serverPool;
1✔
2058
            this.dispatcherFactory = o.dispatcherFactory;
1✔
2059
            this.enableFastFallback = o.enableFastFallback;
1✔
2060
        }
1✔
2061
    }
2062

2063
    // ----------------------------------------------------------------------------------------------------
2064
    // CONSTRUCTOR
2065
    // ----------------------------------------------------------------------------------------------------
2066
    private Options(Builder b) {
1✔
2067
        this.natsServerUris = Collections.unmodifiableList(b.natsServerUris);
1✔
2068
        this.unprocessedServers = Collections.unmodifiableList(b.unprocessedServers);  // exactly how the user gave them
1✔
2069
        this.noRandomize = b.noRandomize;
1✔
2070
        this.noResolveHostnames = b.noResolveHostnames;
1✔
2071
        this.reportNoResponders = b.reportNoResponders;
1✔
2072
        this.connectionName = b.connectionName;
1✔
2073
        this.verbose = b.verbose;
1✔
2074
        this.pedantic = b.pedantic;
1✔
2075
        this.sslContext = b.sslContext;
1✔
2076
        this.maxReconnect = b.maxReconnect;
1✔
2077
        this.reconnectWait = b.reconnectWait;
1✔
2078
        this.reconnectJitter = b.reconnectJitter;
1✔
2079
        this.reconnectJitterTls = b.reconnectJitterTls;
1✔
2080
        this.connectionTimeout = b.connectionTimeout;
1✔
2081
        this.socketReadTimeoutMillis = b.socketReadTimeoutMillis;
1✔
2082
        this.socketWriteTimeout = b.socketWriteTimeout;
1✔
2083
        this.socketSoLinger = b.socketSoLinger;
1✔
2084
        this.pingInterval = b.pingInterval;
1✔
2085
        this.requestCleanupInterval = b.requestCleanupInterval;
1✔
2086
        this.maxPingsOut = b.maxPingsOut;
1✔
2087
        this.reconnectBufferSize = b.reconnectBufferSize;
1✔
2088
        this.username = b.username;
1✔
2089
        this.password = b.password;
1✔
2090
        this.tokenSupplier = b.tokenSupplier;
1✔
2091
        this.useOldRequestStyle = b.useOldRequestStyle;
1✔
2092
        this.maxControlLine = b.maxControlLine;
1✔
2093
        this.bufferSize = b.bufferSize;
1✔
2094
        this.noEcho = b.noEcho;
1✔
2095
        this.noHeaders = b.noHeaders;
1✔
2096
        this.noNoResponders = b.noNoResponders;
1✔
2097
        this.clientSideLimitChecks = b.clientSideLimitChecks;
1✔
2098
        this.supportUTF8Subjects = b.supportUTF8Subjects;
1✔
2099
        this.inboxPrefix = b.inboxPrefix;
1✔
2100
        this.traceConnection = b.traceConnection;
1✔
2101
        this.maxMessagesInOutgoingQueue = b.maxMessagesInOutgoingQueue;
1✔
2102
        this.discardMessagesWhenOutgoingQueueFull = b.discardMessagesWhenOutgoingQueueFull;
1✔
2103

2104
        this.authHandler = b.authHandler;
1✔
2105
        this.reconnectDelayHandler = b.reconnectDelayHandler;
1✔
2106

2107
        this.errorListener = b.errorListener;
1✔
2108
        this.timeTraceLogger = b.timeTraceLogger;
1✔
2109
        this.connectionListener = b.connectionListener;
1✔
2110
        this.readListener = b.readListener;
1✔
2111
        this.statisticsCollector = b.statisticsCollector;
1✔
2112
        this.dataPortType = b.dataPortType;
1✔
2113
        this.trackAdvancedStats = b.trackAdvancedStats;
1✔
2114
        this.executor = b.executor;
1✔
2115
        this.scheduledExecutor = b.scheduledExecutor;
1✔
2116
        this.callbackThreadFactory = b.callbackThreadFactory;
1✔
2117
        this.connectThreadFactory = b.connectThreadFactory;
1✔
2118
        this.httpRequestInterceptors = b.httpRequestInterceptors;
1✔
2119
        this.proxy = b.proxy;
1✔
2120

2121
        this.ignoreDiscoveredServers = b.ignoreDiscoveredServers;
1✔
2122
        this.tlsFirst = b.tlsFirst;
1✔
2123
        this.useTimeoutException = b.useTimeoutException;
1✔
2124
        this.useDispatcherWithExecutor = b.useDispatcherWithExecutor;
1✔
2125
        this.forceFlushOnRequest = b.forceFlushOnRequest;
1✔
2126

2127
        this.serverPool = b.serverPool;
1✔
2128
        this.dispatcherFactory = b.dispatcherFactory;
1✔
2129
        this.enableFastFallback = b.enableFastFallback;
1✔
2130
    }
1✔
2131

2132
    // ----------------------------------------------------------------------------------------------------
2133
    // GETTERS
2134
    // ----------------------------------------------------------------------------------------------------
2135
    /**
2136
     * @return the executor, see {@link Builder#executor(ExecutorService) executor()} in the builder doc
2137
     */
2138
    public ExecutorService getExecutor() {
2139
        return this.executor == null ? _getInternalExecutor() : this.executor;
1✔
2140
    }
2141

2142
    private ExecutorService _getInternalExecutor() {
2143
        String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
2144
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
1✔
2145
            500L, TimeUnit.MILLISECONDS,
2146
            new SynchronousQueue<>(),
2147
            new DefaultThreadFactory(threadPrefix));
2148
    }
2149

2150
    /**
2151
     * @return the ScheduledExecutorService, see {@link Builder#scheduledExecutor(ScheduledExecutorService) scheduledExecutor()} in the builder doc
2152
     */
2153
    public ScheduledExecutorService getScheduledExecutor() {
2154
        return this.scheduledExecutor == null ? _getInternalScheduledExecutor() : this.scheduledExecutor;
1✔
2155
    }
2156

2157
    private ScheduledExecutorService _getInternalScheduledExecutor() {
2158
        String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
2159
        // the core pool size of 3 is chosen considering where we know the scheduler is used.
2160
        // 1. Ping timer, 2. cleanup timer, 3. SocketDataPortWithWriteTimeout
2161
        // Pull message managers also use a scheduler, but we don't even know if this will be consuming
2162
        ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(3, new DefaultThreadFactory(threadPrefix));
1✔
2163
        stpe.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
1✔
2164
        stpe.setRemoveOnCancelPolicy(true);
1✔
2165
        return stpe;
1✔
2166
    }
2167

2168
    public boolean executorIsInternal() {
2169
        return this.executor == null;
1✔
2170
    }
2171

2172
    public boolean scheduledExecutorIsInternal() {
2173
        return this.scheduledExecutor == null;
1✔
2174
    }
2175

2176
    /**
2177
     * @return the callback executor, see {@link Builder#callbackThreadFactory(ThreadFactory) callbackThreadFactory()} in the builder doc
2178
     */
2179
    public ExecutorService getCallbackExecutor() {
2180
        return this.callbackThreadFactory == null ?
1✔
2181
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.callbackThreadFactory);
1✔
2182
    }
2183

2184
    /**
2185
     * @return the connect executor, see {@link Builder#connectThreadFactory(ThreadFactory) connectThreadFactory()} in the builder doc
2186
     */
2187
    public ExecutorService getConnectExecutor() {
2188
        return this.connectThreadFactory == null ?
1✔
2189
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.connectThreadFactory);
1✔
2190
    }
2191

2192
    /**
2193
     * @return the list of HttpRequest interceptors.
2194
     */
2195
    public List<java.util.function.Consumer<HttpRequest>> getHttpRequestInterceptors() {
2196
        return null == this.httpRequestInterceptors
1✔
2197
            ? Collections.emptyList()
1✔
2198
            : Collections.unmodifiableList(this.httpRequestInterceptors);
1✔
2199
    }
2200

2201
    /**
2202
     * @return the proxy to used for all sockets.
2203
     */
2204
    public Proxy getProxy() {
2205
        return this.proxy;
1✔
2206
    }
2207

2208
    /**
2209
     * @return the error listener. Will be an instance of ErrorListenerLoggerImpl if not user supplied. See {@link Builder#errorListener(ErrorListener) errorListener()} in the builder doc
2210
     */
2211
    public ErrorListener getErrorListener() {
2212
        return this.errorListener;
1✔
2213
    }
2214

2215
    /**
2216
     * If the user provided a TimeTraceLogger, it's returned here.
2217
     * If the user set traceConnection but did not supply their own, the original time trace logging will occur
2218
     * If the user did not provide a TimeTraceLogger and did not set traceConnection, this will be a no-op implementation.
2219
     * @return the time trace logger
2220
     */
2221
    public TimeTraceLogger getTimeTraceLogger() {
2222
        return this.timeTraceLogger;
1✔
2223
    }
2224

2225
    /**
2226
     * @return the connection listener, or null, see {@link Builder#connectionListener(ConnectionListener) connectionListener()} in the builder doc
2227
     */
2228
    public ConnectionListener getConnectionListener() {
2229
        return this.connectionListener;
1✔
2230
    }
2231

2232
    /**
2233
     * @return the read listener, or null, see {@link Builder#readListener(ReadListener) readListener()} in the builder doc
2234
     */
2235
    public ReadListener getReadListener() {
2236
        return this.readListener;
1✔
2237
    }
2238

2239
    /**
2240
     * @return the statistics collector, or null, see {@link Builder#statisticsCollector(StatisticsCollector) statisticsCollector()} in the builder doc
2241
     */
2242
    public StatisticsCollector getStatisticsCollector() {
2243
        return this.statisticsCollector;
1✔
2244
    }
2245

2246
    /**
2247
     * @return the auth handler, or null, see {@link Builder#authHandler(AuthHandler) authHandler()} in the builder doc
2248
     */
2249
    public AuthHandler getAuthHandler() {
2250
        return this.authHandler;
1✔
2251
    }
2252

2253
    /**
2254
     * @return the reconnection delay handler, or null, see {@link Builder#reconnectDelayHandler(ReconnectDelayHandler) reconnectDelayHandler()} in the builder doc
2255
     */
2256
    public ReconnectDelayHandler getReconnectDelayHandler() {
2257
        return this.reconnectDelayHandler;
1✔
2258
    }
2259

2260
    /**
2261
     * @return the dataport type for connections created by this options object, see {@link Builder#dataPortType(String) dataPortType()} in the builder doc
2262
     */
2263
    public String getDataPortType() {
2264
        return this.dataPortType;
1✔
2265
    }
2266

2267
    /**
2268
     * @return the data port described by these options
2269
     */
2270
    public DataPort buildDataPort() {
2271
        DataPort dp;
2272
        if (dataPortType.equals(DEFAULT_DATA_PORT_TYPE)) {
1✔
2273
            if (socketWriteTimeout == null) {
1✔
2274
                dp = new SocketDataPort();
1✔
2275
            }
2276
            else {
2277
                dp = new SocketDataPortWithWriteTimeout();
1✔
2278
            }
2279
        }
2280
        else {
2281
            dp = (DataPort) Options.createInstanceOf(dataPortType);
1✔
2282
        }
2283
        dp.afterConstruct(this);
1✔
2284
        return dp;
1✔
2285
    }
2286

2287
    /**
2288
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2289
     */
2290
    public List<URI> getServers() {
2291
        List<URI> list = new ArrayList<>();
1✔
2292
        for (NatsUri nuri : natsServerUris) {
1✔
2293
            list.add(nuri.getUri());
1✔
2294
        }
1✔
2295
        return list;
1✔
2296
    }
2297

2298
    /**
2299
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2300
     */
2301
    public List<NatsUri> getNatsServerUris() {
2302
        return natsServerUris;
1✔
2303
    }
2304

2305
    /**
2306
     * @return the servers as given to the options, since the servers are normalized
2307
     */
2308
    public List<String> getUnprocessedServers() {
2309
        return unprocessedServers;
1✔
2310
    }
2311

2312
    /**
2313
     * @return should we turn off randomization for server connection attempts, see {@link Builder#noRandomize() noRandomize()} in the builder doc
2314
     */
2315
    public boolean isNoRandomize() {
2316
        return noRandomize;
1✔
2317
    }
2318

2319
    /**
2320
     * @return should we resolve hostnames for server connection attempts, see {@link Builder#noResolveHostnames() noResolveHostnames()} in the builder doc
2321
     */
2322
    public boolean isNoResolveHostnames() {
2323
        return noResolveHostnames;
1✔
2324
    }
2325

2326
    /**
2327
     * @return should complete with exception futures for requests that get no responders instead of cancelling the future, see {@link Builder#reportNoResponders() reportNoResponders()} in the builder doc
2328
     */
2329
    public boolean isReportNoResponders() {
2330
        return reportNoResponders;
1✔
2331
    }
2332

2333
    /**
2334
     * @return the connectionName, see {@link Builder#connectionName(String) connectionName()} in the builder doc
2335
     */
2336
    public String getConnectionName() {
2337
        return connectionName;
1✔
2338
    }
2339

2340
    /**
2341
     * @return are we in verbose mode, see {@link Builder#verbose() verbose()} in the builder doc
2342
     */
2343
    public boolean isVerbose() {
2344
        return verbose;
1✔
2345
    }
2346

2347
    /**
2348
     * @return is echo-ing disabled, see {@link Builder#noEcho() noEcho()} in the builder doc
2349
     */
2350
    public boolean isNoEcho() {
2351
        return noEcho;
1✔
2352
    }
2353

2354
    /**
2355
     * @return are headers disabled, see {@link Builder#noHeaders() noHeaders()} in the builder doc
2356
     */
2357
    public boolean isNoHeaders() {
2358
        return noHeaders;
1✔
2359
    }
2360

2361
    /**
2362
     * @return is NoResponders ignored disabled, see {@link Builder#noNoResponders() noNoResponders()} in the builder doc
2363
     */
2364
    public boolean isNoNoResponders() {
2365
        return noNoResponders;
1✔
2366
    }
2367

2368
    /**
2369
     * @return clientSideLimitChecks flag
2370
     */
2371
    public boolean clientSideLimitChecks() {
2372
        return clientSideLimitChecks;
1✔
2373
    }
2374

2375
    /**
2376
     * @return whether utf8 subjects are supported, see {@link Builder#supportUTF8Subjects() supportUTF8Subjects()} in the builder doc.
2377
     */
2378
    public boolean supportUTF8Subjects() {
2379
        return supportUTF8Subjects;
1✔
2380
    }
2381

2382
    /**
2383
     * @return are we using pedantic protocol, see {@link Builder#pedantic() pedantic()} in the builder doc
2384
     */
2385
    public boolean isPedantic() {
2386
        return pedantic;
1✔
2387
    }
2388

2389
    /**
2390
     * @return should we track advanced stats, see {@link Builder#turnOnAdvancedStats() turnOnAdvancedStats()} in the builder doc
2391
     */
2392
    public boolean isTrackAdvancedStats() {
2393
        return trackAdvancedStats;
1✔
2394
    }
2395

2396
    /**
2397
     * If isTraceConnection is true, the user provided a TimeTraceLogger or manually called traceConnection in the builder
2398
     * @return should we trace the connection?
2399
     */
2400
    public boolean isTraceConnection() {
2401
        return traceConnection;
1✔
2402
    }
2403

2404
    /**
2405
     * @return the maximum length of a control line, see {@link Builder#maxControlLine(int) maxControlLine()} in the builder doc
2406
     */
2407
    public int getMaxControlLine() {
2408
        return maxControlLine;
1✔
2409
    }
2410

2411
    /**
2412
     *
2413
     * @return true if there is an sslContext for these Options, otherwise false, see {@link Builder#secure() secure()} in the builder doc
2414
     */
2415
    public boolean isTLSRequired() {
2416
        return sslContext != null;
1✔
2417
    }
2418

2419
    /**
2420
     * @return the sslContext, see {@link Builder#secure() secure()} in the builder doc
2421
     */
2422
    public SSLContext getSslContext() {
2423
        return sslContext;
1✔
2424
    }
2425

2426
    /**
2427
     * @return the maxReconnect attempts to make before failing, see {@link Builder#maxReconnects(int) maxReconnects()} in the builder doc
2428
     */
2429
    public int getMaxReconnect() {
2430
        return maxReconnect;
1✔
2431
    }
2432

2433
    /**
2434
     * @return the reconnectWait, used between reconnect attempts, see {@link Builder#reconnectWait(Duration) reconnectWait()} in the builder doc
2435
     */
2436
    public Duration getReconnectWait() {
2437
        return reconnectWait;
1✔
2438
    }
2439

2440
    /**
2441
     * @return the reconnectJitter, used between reconnect attempts to vary the reconnect wait, see {@link Builder#reconnectJitter(Duration) reconnectJitter()} in the builder doc
2442
     */
2443
    public Duration getReconnectJitter() {
2444
        return reconnectJitter;
1✔
2445
    }
2446

2447
    /**
2448
     * @return the reconnectJitterTls, used between reconnect attempts to vary the reconnect wait whe using tls/secure, see {@link Builder#reconnectJitterTls(Duration) reconnectJitterTls()} in the builder doc
2449
     */
2450
    public Duration getReconnectJitterTls() {
2451
        return reconnectJitterTls;
1✔
2452
    }
2453

2454
    /**
2455
     * @return the connectionTimeout, see {@link Builder#connectionTimeout(Duration) connectionTimeout()} in the builder doc
2456
     */
2457
    public Duration getConnectionTimeout() {
2458
        return connectionTimeout;
1✔
2459
    }
2460

2461
    /**
2462
     * @return the socketReadTimeoutMillis, see {@link Builder#socketReadTimeoutMillis(int) socketReadTimeoutMillis} in the builder doc
2463
     */
2464
    public int getSocketReadTimeoutMillis() {
2465
        return socketReadTimeoutMillis;
1✔
2466
    }
2467

2468
    /**
2469
     * @return the socketWriteTimeout, see {@link Builder#socketWriteTimeout(long) socketWriteTimeout} in the builder doc
2470
     */
2471
    public Duration getSocketWriteTimeout() {
2472
        return socketWriteTimeout;
1✔
2473
    }
2474

2475
    /**
2476
     * @return the socket so linger number of seconds, see {@link Builder#socketSoLinger(int) socketSoLinger()} in the builder doc
2477
     */
2478
    public int getSocketSoLinger() {
2479
        return socketSoLinger;
1✔
2480
    }
2481

2482
    /**
2483
     * @return the pingInterval, see {@link Builder#pingInterval(Duration) pingInterval()} in the builder doc
2484
     */
2485
    public Duration getPingInterval() {
2486
        return pingInterval;
1✔
2487
    }
2488

2489
    /**
2490
     * @return the request cleanup interval, see {@link Builder#requestCleanupInterval(Duration) requestCleanupInterval()} in the builder doc
2491
     */
2492
    public Duration getRequestCleanupInterval() {
2493
        return requestCleanupInterval;
1✔
2494
    }
2495

2496
    /**
2497
     * @return the maxPingsOut to limit the number of pings on the wire, see {@link Builder#maxPingsOut(int) maxPingsOut()} in the builder doc
2498
     */
2499
    public int getMaxPingsOut() {
2500
        return maxPingsOut;
1✔
2501
    }
2502

2503
    /**
2504
     * @return the reconnectBufferSize, to limit the amount of data held during
2505
     *         reconnection attempts, see {@link Builder#reconnectBufferSize(long) reconnectBufferSize()} in the builder doc
2506
     */
2507
    public long getReconnectBufferSize() {
2508
        return reconnectBufferSize;
1✔
2509
    }
2510

2511
    /**
2512
     * @return the default size for buffers in the connection code, see {@link Builder#bufferSize(int) bufferSize()} in the builder doc
2513
     */
2514
    public int getBufferSize() {
2515
        return bufferSize;
1✔
2516
    }
2517

2518
    /**
2519
     * @deprecated converts the char array to a string, use getUserNameChars instead for more security
2520
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2521
     */
2522
    @Deprecated
2523
    public String getUsername() {
2524
        return username == null ? null : new String(username);
1✔
2525
    }
2526

2527
    /**
2528
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2529
     */
2530
    public char[] getUsernameChars() {
2531
        return username;
1✔
2532
    }
2533

2534
    /**
2535
     * @deprecated converts the char array to a string, use getPasswordChars instead for more security
2536
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2537
     */
2538
    @Deprecated
2539
    public String getPassword() {
2540
        return password == null ? null : new String(password);
1✔
2541
    }
2542

2543
    /**
2544
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2545
     */
2546
    public char[] getPasswordChars() {
2547
        return password;
1✔
2548
    }
2549

2550
    /**
2551
     * @deprecated converts the char array to a string, use getTokenChars instead for more security
2552
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2553
     */
2554
    @Deprecated
2555
    public String getToken() {
2556
        char[] token = tokenSupplier.get();
1✔
2557
        return token == null ? null : new String(token);
1✔
2558
    }
2559

2560
    /**
2561
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2562
     */
2563
    public char[] getTokenChars() {
2564
        return tokenSupplier.get();
1✔
2565
    }
2566

2567
    /**
2568
     * @return the flag to turn on old style requests, see {@link Builder#oldRequestStyle() oldStyleRequest()} in the builder doc
2569
     */
2570
    public boolean isOldRequestStyle() {
2571
        return useOldRequestStyle;
1✔
2572
    }
2573

2574
    /**
2575
     * @return the inbox prefix to use for requests, see {@link Builder#inboxPrefix(String) inboxPrefix()} in the builder doc
2576
     */
2577
    public String getInboxPrefix() {
2578
        return inboxPrefix;
1✔
2579
    }
2580

2581
    /**
2582
     * @return the maximum number of messages in the outgoing queue, see {@link Builder#maxMessagesInOutgoingQueue(int)
2583
     * maxMessagesInOutgoingQueue(int)} in the builder doc
2584
     */
2585
    public int getMaxMessagesInOutgoingQueue() {
2586
        return maxMessagesInOutgoingQueue;
1✔
2587
    }
2588

2589
    /**
2590
     * @return should we discard messages when the outgoing queue is full, see {@link Builder#discardMessagesWhenOutgoingQueueFull()
2591
     * discardMessagesWhenOutgoingQueueFull()} in the builder doc
2592
     */
2593
    public boolean isDiscardMessagesWhenOutgoingQueueFull() {
2594
        return discardMessagesWhenOutgoingQueueFull;
1✔
2595
    }
2596

2597
    /**
2598
     * Get whether to ignore discovered servers
2599
     * @return the flag
2600
     */
2601
    public boolean isIgnoreDiscoveredServers() {
2602
        return ignoreDiscoveredServers;
1✔
2603
    }
2604

2605
    /**
2606
     * Get whether to do tls first
2607
     * @return the flag
2608
     */
2609
    public boolean isTlsFirst() {
2610
        return tlsFirst;
1✔
2611
    }
2612

2613
    /**
2614
     * Get whether to throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}.
2615
     * @return the flag
2616
     */
2617
    public boolean useTimeoutException() {
2618
        return useTimeoutException;
1✔
2619
    }
2620

2621
    /**
2622
     * Whether the dispatcher should use an executor to async messages to handlers
2623
     * @return the flag
2624
     */
2625
    public boolean useDispatcherWithExecutor() { return useDispatcherWithExecutor; }
1✔
2626

2627
    /**
2628
     * Whether to flush on any user request
2629
     * @return the flag
2630
     */
2631
    public boolean forceFlushOnRequest() {
2632
        return forceFlushOnRequest;
1✔
2633
    }
2634

2635
    /**
2636
     * Get the ServerPool implementation. If null, a default implementation is used.
2637
     * @return the ServerPool implementation
2638
     */
2639
    public ServerPool getServerPool() {
2640
        return serverPool;
1✔
2641
    }
2642

2643
    /**
2644
     * Get the DispatcherFactory implementation. If null, a default implementation is used.
2645
     * @return the DispatcherFactory implementation
2646
     */
2647
    public DispatcherFactory getDispatcherFactory() {
2648
        return dispatcherFactory;
1✔
2649
    }
2650

2651
    /**
2652
     * Whether Fast fallback algorithm is enabled for socket connect
2653
     * @return the flag
2654
     */
2655
    public boolean isEnableFastFallback() {
2656
        return enableFastFallback;
1✔
2657
    }
2658

2659
    public URI createURIForServer(String serverURI) throws URISyntaxException {
2660
        return new NatsUri(serverURI).getUri();
1✔
2661
    }
2662

2663
    /**
2664
     * Create the options string sent with the connect message.
2665
     * If includeAuth is true the auth information is included:
2666
     * If the server URIs have auth info it is used. Otherwise, the userInfo is used.
2667
     * @param serverURI the current server uri
2668
     * @param includeAuth tells the options to build a connection string that includes auth information
2669
     * @param nonce if the client is supposed to sign the nonce for authentication
2670
     * @return the options String, basically JSON
2671
     */
2672
    public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean includeAuth, byte[] nonce) {
2673
        CharBuffer connectString = CharBuffer.allocate(this.maxControlLine);
1✔
2674
        connectString.append("{");
1✔
2675

2676
        appendOption(connectString, Options.OPTION_LANG, Nats.CLIENT_LANGUAGE, true, false);
1✔
2677
        appendOption(connectString, Options.OPTION_VERSION, Nats.CLIENT_VERSION, true, true);
1✔
2678

2679
        if (this.connectionName != null) {
1✔
2680
            appendOption(connectString, Options.OPTION_NAME, this.connectionName, true, true);
1✔
2681
        }
2682

2683
        appendOption(connectString, Options.OPTION_PROTOCOL, "1", false, true);
1✔
2684

2685
        appendOption(connectString, Options.OPTION_VERBOSE, String.valueOf(this.isVerbose()), false, true);
1✔
2686
        appendOption(connectString, Options.OPTION_PEDANTIC, String.valueOf(this.isPedantic()), false, true);
1✔
2687
        appendOption(connectString, Options.OPTION_TLS_REQUIRED, String.valueOf(this.isTLSRequired()), false, true);
1✔
2688
        appendOption(connectString, Options.OPTION_ECHO, String.valueOf(!this.isNoEcho()), false, true);
1✔
2689
        appendOption(connectString, Options.OPTION_HEADERS, String.valueOf(!this.isNoHeaders()), false, true);
1✔
2690
        appendOption(connectString, Options.OPTION_NORESPONDERS, String.valueOf(!this.isNoNoResponders()), false, true);
1✔
2691

2692
        if (includeAuth) {
1✔
2693
            if (nonce != null && this.getAuthHandler() != null) {
1✔
2694
                char[] nkey = this.getAuthHandler().getID();
1✔
2695
                byte[] sig = this.getAuthHandler().sign(nonce);
1✔
2696
                char[] jwt = this.getAuthHandler().getJWT();
1✔
2697

2698
                if (sig == null) {
1✔
2699
                    sig = new byte[0];
1✔
2700
                }
2701

2702
                if (jwt == null) {
1✔
2703
                    jwt = new char[0];
1✔
2704
                }
2705

2706
                if (nkey == null) {
1✔
2707
                    nkey = new char[0];
1✔
2708
                }
2709

2710
                String encodedSig = base64UrlEncodeToString(sig);
1✔
2711

2712
                appendOption(connectString, Options.OPTION_NKEY, nkey, true);
1✔
2713
                appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
1✔
2714
                appendOption(connectString, Options.OPTION_JWT, jwt, true);
1✔
2715
            }
2716

2717
            String uriUser = null;
1✔
2718
            String uriPass = null;
1✔
2719
            String uriToken = null;
1✔
2720

2721
            // Values from URI override options
2722
            try {
2723
                URI uri = this.createURIForServer(serverURI);
1✔
2724
                String userInfo = uri.getRawUserInfo();
1✔
2725
                if (userInfo != null) {
1✔
2726
                    int at = userInfo.indexOf(":");
1✔
2727
                    if (at == -1) {
1✔
2728
                        uriToken = uriDecode(userInfo);
1✔
2729
                    }
2730
                    else {
2731
                        uriUser = uriDecode(userInfo.substring(0, at));
1✔
2732
                        uriPass = uriDecode(userInfo.substring(at + 1));
1✔
2733
                    }
2734
                }
2735
            }
2736
            catch (URISyntaxException e) {
×
2737
                // the createURIForServer call is the one that potentially throws this
2738
                // uriUser, uriPass and uriToken will already be null
2739
            }
1✔
2740

2741
            if (uriUser != null) {
1✔
2742
                appendOption(connectString, Options.OPTION_USER, jsonEncode(uriUser), true, true);
1✔
2743
            }
2744
            else if (this.username != null) {
1✔
2745
                appendOption(connectString, Options.OPTION_USER, jsonEncode(this.username), true, true);
1✔
2746
            }
2747

2748
            if (uriPass != null) {
1✔
2749
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(uriPass), true, true);
1✔
2750
            }
2751
            else if (this.password != null) {
1✔
2752
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(this.password), true, true);
1✔
2753
            }
2754

2755
            if (uriToken != null) {
1✔
2756
                appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
1✔
2757
            }
2758
            else {
2759
                char[] token = this.tokenSupplier.get();
1✔
2760
                if (token != null) {
1✔
2761
                    appendOption(connectString, Options.OPTION_AUTH_TOKEN, token, true);
1✔
2762
                }
2763
            }
2764
        }
2765

2766
        connectString.append("}");
1✔
2767
        connectString.flip();
1✔
2768
        return connectString;
1✔
2769
    }
2770

2771
    // ----------------------------------------------------------------------------------------------------
2772
    // HELPER FUNCTIONS
2773
    // ----------------------------------------------------------------------------------------------------
2774
    private static void appendOption(CharBuffer builder, String key, String value, boolean quotes, boolean comma) {
2775
        _appendStart(builder, key, quotes, comma);
1✔
2776
        builder.append(value);
1✔
2777
        _appendOptionEnd(builder, quotes);
1✔
2778
    }
1✔
2779

2780
    @SuppressWarnings("SameParameterValue")
2781
    private static void appendOption(CharBuffer builder, String key, char[] value, boolean comma) {
2782
        _appendStart(builder, key, true, comma);
1✔
2783
        builder.put(value);
1✔
2784
        _appendOptionEnd(builder, true);
1✔
2785
    }
1✔
2786

2787
    private static void _appendStart(CharBuffer builder, String key, boolean quotes, boolean comma) {
2788
        if (comma) {
1✔
2789
            builder.append(',');
1✔
2790
        }
2791
        builder.append('"');
1✔
2792
        builder.append(key);
1✔
2793
        builder.append('"');
1✔
2794
        builder.append(':');
1✔
2795
        _appendOptionEnd(builder, quotes);
1✔
2796
    }
1✔
2797

2798
    private static void _appendOptionEnd(CharBuffer builder, boolean quotes) {
2799
        if (quotes) {
1✔
2800
            builder.append('"');
1✔
2801
        }
2802
    }
1✔
2803

2804
    private static String getPropertyValue(Properties props, String key) {
2805
        String value = emptyAsNull(props.getProperty(key));
1✔
2806
        if (value != null) {
1✔
2807
            return value;
1✔
2808
        }
2809
        if (key.startsWith(PFX)) { // if the key starts with the PFX, check the non PFX
1✔
2810
            return emptyAsNull(props.getProperty(key.substring(PFX_LEN)));
1✔
2811
        }
2812
        // otherwise check with the PFX
2813
        value = emptyAsNull(props.getProperty(PFX + key));
1✔
2814
        if (value == null && key.contains("_")) {
1✔
2815
            // addressing where underscore was used in a key value instead of dot
2816
            return getPropertyValue(props, key.replace("_", "."));
1✔
2817
        }
2818
        return value;
1✔
2819
    }
2820

2821
    private static void stringProperty(Properties props, String key, java.util.function.Consumer<String> consumer) {
2822
        String value = getPropertyValue(props, key);
1✔
2823
        if (value != null) {
1✔
2824
            consumer.accept(value);
1✔
2825
        }
2826
    }
1✔
2827

2828
    private static void charArrayProperty(Properties props, String key, java.util.function.Consumer<char[]> consumer) {
2829
        String value = getPropertyValue(props, key);
1✔
2830
        if (value != null) {
1✔
2831
            consumer.accept(value.toCharArray());
1✔
2832
        }
2833
    }
1✔
2834

2835
    private static void booleanProperty(Properties props, String key, java.util.function.Consumer<Boolean> consumer) {
2836
        String value = getPropertyValue(props, key);
1✔
2837
        if (value != null) {
1✔
2838
            consumer.accept(Boolean.parseBoolean(value));
1✔
2839
        }
2840
    }
1✔
2841

2842
    private static void intProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2843
        String value = getPropertyValue(props, key);
1✔
2844
        if (value == null) {
1✔
2845
            consumer.accept(defaultValue);
1✔
2846
        }
2847
        else {
2848
            consumer.accept(Integer.parseInt(value));
1✔
2849
        }
2850
    }
1✔
2851

2852
    private static void intGtEqZeroProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2853
        String value = getPropertyValue(props, key);
1✔
2854
        if (value == null) {
1✔
2855
            consumer.accept(defaultValue);
1✔
2856
        }
2857
        else {
2858
            int i = Integer.parseInt(value);
1✔
2859
            if (i < 0) {
1✔
2860
                consumer.accept(defaultValue);
1✔
2861
            }
2862
            else {
2863
                consumer.accept(i);
1✔
2864
            }
2865
        }
2866
    }
1✔
2867

2868
    private static void longProperty(Properties props, String key, long defaultValue, java.util.function.Consumer<Long> consumer) {
2869
        String value = getPropertyValue(props, key);
1✔
2870
        if (value == null) {
1✔
2871
            consumer.accept(defaultValue);
1✔
2872
        }
2873
        else {
2874
            consumer.accept(Long.parseLong(value));
1✔
2875
        }
2876
    }
1✔
2877

2878
    private static void durationProperty(Properties props, String key, Duration defaultValue, java.util.function.Consumer<Duration> consumer) {
2879
        String value = getPropertyValue(props, key);
1✔
2880
        if (value == null) {
1✔
2881
            consumer.accept(defaultValue);
1✔
2882
        }
2883
        else {
2884
            try {
2885
                Duration d = Duration.parse(value);
1✔
2886
                if (d.toNanos() < 0) {
1✔
2887
                    consumer.accept(defaultValue);
×
2888
                }
2889
                else {
2890
                    consumer.accept(d);
1✔
2891
                }
2892
            }
2893
            catch (DateTimeParseException pe) {
1✔
2894
                int ms = Integer.parseInt(value);
1✔
2895
                if (ms < 0) {
1✔
2896
                    consumer.accept(defaultValue);
1✔
2897
                }
2898
                else {
2899
                    consumer.accept(Duration.ofMillis(ms));
1✔
2900
                }
2901
            }
1✔
2902
        }
2903
    }
1✔
2904

2905
    private static void classnameProperty(Properties props, String key, java.util.function.Consumer<Object> consumer) {
2906
        stringProperty(props, key, className -> consumer.accept(createInstanceOf(className)));
1✔
2907
    }
1✔
2908

2909
    private static Object createInstanceOf(String className) {
2910
        try {
2911
            Class<?> clazz = Class.forName(className);
1✔
2912
            Constructor<?> constructor = clazz.getConstructor();
1✔
2913
            return constructor.newInstance();
1✔
2914
        } catch (Exception e) {
1✔
2915
            throw new IllegalArgumentException(e);
1✔
2916
        }
2917
    }
2918
}
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