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

nats-io / nats.java / #2170

09 Sep 2025 12:23PM UTC coverage: 95.143% (-0.3%) from 95.433%
#2170

push

github

web-flow
Merge pull request #1415 from nats-io/stats-options-review

Statistics classes improvements

3 of 40 new or added lines in 3 files covered. (7.5%)

4 existing lines in 2 files now uncovered.

11970 of 12581 relevant lines covered (95.14%)

0.95 hits per line

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

96.35
/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
     * Constant used for calculating if a socket read timeout is large enough.
130
     */
131
    public static final long MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
132

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

821
        private AuthHandler authHandler;
822
        private ReconnectDelayHandler reconnectDelayHandler;
823

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

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

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

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

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

877
        // ----------------------------------------------------------------------------------------------------
878
        // BUILDER METHODS
879
        // ----------------------------------------------------------------------------------------------------
880

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1953
            if (socketReadTimeoutMillis > 0) {
1✔
1954
                long srtMin = pingInterval.toMillis() + MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT;
1✔
1955
                if (socketReadTimeoutMillis < srtMin) {
1✔
1956
                    throw new IllegalStateException("Socket Read Timeout must be at least "
1✔
1957
                        + MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT
1958
                        + " milliseconds greater than the Ping Interval");
1959
                }
1960
            }
1961

1962
            if (socketWriteTimeout != null && socketWriteTimeout.toMillis() < 1) {
1✔
UNCOV
1963
                socketWriteTimeout = null;
×
1964
            }
1965

1966
            if (socketSoLinger < 0) {
1✔
1967
                socketSoLinger = -1;
1✔
1968
            }
1969

1970
            if (errorListener == null) {
1✔
1971
                errorListener = new ErrorListenerLoggerImpl();
1✔
1972
            }
1973

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

1990
            return new Options(this);
1✔
1991
        }
1992

1993
        // ----------------------------------------------------------------------------------------------------
1994
        // BUILDER COPY CONSTRUCTOR
1995
        // ----------------------------------------------------------------------------------------------------
1996
        public Builder(Options o) {
1✔
1997
            if (o == null) {
1✔
1998
                throw new IllegalArgumentException("Options cannot be null");
1✔
1999
            }
2000

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

2038
            this.authHandler = o.authHandler;
1✔
2039
            this.reconnectDelayHandler = o.reconnectDelayHandler;
1✔
2040

2041
            this.errorListener = o.errorListener;
1✔
2042
            this.timeTraceLogger = o.timeTraceLogger;
1✔
2043
            this.connectionListener = o.connectionListener;
1✔
2044
            this.readListener = o.readListener;
1✔
2045
            this.statisticsCollector = o.statisticsCollector;
1✔
2046
            this.dataPortType = o.dataPortType;
1✔
2047
            this.trackAdvancedStats = o.trackAdvancedStats;
1✔
2048
            this.executor = o.executor;
1✔
2049
            this.scheduledExecutor = o.scheduledExecutor;
1✔
2050
            this.callbackThreadFactory = o.callbackThreadFactory;
1✔
2051
            this.connectThreadFactory = o.connectThreadFactory;
1✔
2052
            this.httpRequestInterceptors = o.httpRequestInterceptors;
1✔
2053
            this.proxy = o.proxy;
1✔
2054

2055
            this.ignoreDiscoveredServers = o.ignoreDiscoveredServers;
1✔
2056
            this.tlsFirst = o.tlsFirst;
1✔
2057
            this.useTimeoutException = o.useTimeoutException;
1✔
2058
            this.useDispatcherWithExecutor = o.useDispatcherWithExecutor;
1✔
2059
            this.forceFlushOnRequest = o.forceFlushOnRequest;
1✔
2060

2061
            this.serverPool = o.serverPool;
1✔
2062
            this.dispatcherFactory = o.dispatcherFactory;
1✔
2063
            this.enableFastFallback = o.enableFastFallback;
1✔
2064
        }
1✔
2065
    }
2066

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

2108
        this.authHandler = b.authHandler;
1✔
2109
        this.reconnectDelayHandler = b.reconnectDelayHandler;
1✔
2110

2111
        this.errorListener = b.errorListener;
1✔
2112
        this.timeTraceLogger = b.timeTraceLogger;
1✔
2113
        this.connectionListener = b.connectionListener;
1✔
2114
        this.readListener = b.readListener;
1✔
2115
        this.statisticsCollector = b.statisticsCollector;
1✔
2116
        this.dataPortType = b.dataPortType;
1✔
2117
        this.trackAdvancedStats = b.trackAdvancedStats;
1✔
2118
        this.executor = b.executor;
1✔
2119
        this.scheduledExecutor = b.scheduledExecutor;
1✔
2120
        this.callbackThreadFactory = b.callbackThreadFactory;
1✔
2121
        this.connectThreadFactory = b.connectThreadFactory;
1✔
2122
        this.httpRequestInterceptors = b.httpRequestInterceptors;
1✔
2123
        this.proxy = b.proxy;
1✔
2124

2125
        this.ignoreDiscoveredServers = b.ignoreDiscoveredServers;
1✔
2126
        this.tlsFirst = b.tlsFirst;
1✔
2127
        this.useTimeoutException = b.useTimeoutException;
1✔
2128
        this.useDispatcherWithExecutor = b.useDispatcherWithExecutor;
1✔
2129
        this.forceFlushOnRequest = b.forceFlushOnRequest;
1✔
2130

2131
        this.serverPool = b.serverPool;
1✔
2132
        this.dispatcherFactory = b.dispatcherFactory;
1✔
2133
        this.enableFastFallback = b.enableFastFallback;
1✔
2134
    }
1✔
2135

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

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

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

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

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

2176
    public boolean scheduledExecutorIsInternal() {
2177
        return this.scheduledExecutor == null;
1✔
2178
    }
2179

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

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

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

2205
    /**
2206
     * @return the proxy to used for all sockets.
2207
     */
2208
    public Proxy getProxy() {
2209
        return this.proxy;
1✔
2210
    }
2211

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

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

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

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

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

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

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

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

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

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

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

2309
    /**
2310
     * @return the servers as given to the options, since the servers are normalized
2311
     */
2312
    public List<String> getUnprocessedServers() {
2313
        return unprocessedServers;
1✔
2314
    }
2315

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

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

2330
    /**
2331
     * @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
2332
     */
2333
    public boolean isReportNoResponders() {
2334
        return reportNoResponders;
1✔
2335
    }
2336

2337
    /**
2338
     * @return the connectionName, see {@link Builder#connectionName(String) connectionName()} in the builder doc
2339
     */
2340
    public String getConnectionName() {
2341
        return connectionName;
1✔
2342
    }
2343

2344
    /**
2345
     * @return are we in verbose mode, see {@link Builder#verbose() verbose()} in the builder doc
2346
     */
2347
    public boolean isVerbose() {
2348
        return verbose;
1✔
2349
    }
2350

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

2358
    /**
2359
     * @return are headers disabled, see {@link Builder#noHeaders() noHeaders()} in the builder doc
2360
     */
2361
    public boolean isNoHeaders() {
2362
        return noHeaders;
1✔
2363
    }
2364

2365
    /**
2366
     * @return is NoResponders ignored disabled, see {@link Builder#noNoResponders() noNoResponders()} in the builder doc
2367
     */
2368
    public boolean isNoNoResponders() {
2369
        return noNoResponders;
1✔
2370
    }
2371

2372
    /**
2373
     * @return clientSideLimitChecks flag
2374
     */
2375
    public boolean clientSideLimitChecks() {
2376
        return clientSideLimitChecks;
1✔
2377
    }
2378

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

2386
    /**
2387
     * @return are we using pedantic protocol, see {@link Builder#pedantic() pedantic()} in the builder doc
2388
     */
2389
    public boolean isPedantic() {
2390
        return pedantic;
1✔
2391
    }
2392

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

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

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

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

2423
    /**
2424
     * @return the sslContext, see {@link Builder#secure() secure()} in the builder doc
2425
     */
2426
    public SSLContext getSslContext() {
2427
        return sslContext;
1✔
2428
    }
2429

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

2437
    /**
2438
     * @return the reconnectWait, used between reconnect attempts, see {@link Builder#reconnectWait(Duration) reconnectWait()} in the builder doc
2439
     */
2440
    public Duration getReconnectWait() {
2441
        return reconnectWait;
1✔
2442
    }
2443

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

2451
    /**
2452
     * @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
2453
     */
2454
    public Duration getReconnectJitterTls() {
2455
        return reconnectJitterTls;
1✔
2456
    }
2457

2458
    /**
2459
     * @return the connectionTimeout, see {@link Builder#connectionTimeout(Duration) connectionTimeout()} in the builder doc
2460
     */
2461
    public Duration getConnectionTimeout() {
2462
        return connectionTimeout;
1✔
2463
    }
2464

2465
    /**
2466
     * @return the socketReadTimeoutMillis, see {@link Builder#socketReadTimeoutMillis(int) socketReadTimeoutMillis} in the builder doc
2467
     */
2468
    public int getSocketReadTimeoutMillis() {
2469
        return socketReadTimeoutMillis;
1✔
2470
    }
2471

2472
    /**
2473
     * @return the socketWriteTimeout, see {@link Builder#socketWriteTimeout(long) socketWriteTimeout} in the builder doc
2474
     */
2475
    public Duration getSocketWriteTimeout() {
2476
        return socketWriteTimeout;
1✔
2477
    }
2478

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

2486
    /**
2487
     * @return the pingInterval, see {@link Builder#pingInterval(Duration) pingInterval()} in the builder doc
2488
     */
2489
    public Duration getPingInterval() {
2490
        return pingInterval;
1✔
2491
    }
2492

2493
    /**
2494
     * @return the request cleanup interval, see {@link Builder#requestCleanupInterval(Duration) requestCleanupInterval()} in the builder doc
2495
     */
2496
    public Duration getRequestCleanupInterval() {
2497
        return requestCleanupInterval;
1✔
2498
    }
2499

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

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

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

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

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

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

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

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

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

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

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

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

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

2601
    /**
2602
     * Get whether to ignore discovered servers
2603
     * @return the flag
2604
     */
2605
    public boolean isIgnoreDiscoveredServers() {
2606
        return ignoreDiscoveredServers;
1✔
2607
    }
2608

2609
    /**
2610
     * Get whether to do tls first
2611
     * @return the flag
2612
     */
2613
    public boolean isTlsFirst() {
2614
        return tlsFirst;
1✔
2615
    }
2616

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

2625
    /**
2626
     * Whether the dispatcher should use an executor to async messages to handlers
2627
     * @return the flag
2628
     */
2629
    public boolean useDispatcherWithExecutor() { return useDispatcherWithExecutor; }
1✔
2630

2631
    /**
2632
     * Whether to flush on any user request
2633
     * @return the flag
2634
     */
2635
    public boolean forceFlushOnRequest() {
2636
        return forceFlushOnRequest;
1✔
2637
    }
2638

2639
    /**
2640
     * Get the ServerPool implementation. If null, a default implementation is used.
2641
     * @return the ServerPool implementation
2642
     */
2643
    public ServerPool getServerPool() {
2644
        return serverPool;
1✔
2645
    }
2646

2647
    /**
2648
     * Get the DispatcherFactory implementation. If null, a default implementation is used.
2649
     * @return the DispatcherFactory implementation
2650
     */
2651
    public DispatcherFactory getDispatcherFactory() {
2652
        return dispatcherFactory;
1✔
2653
    }
2654

2655
    /**
2656
     * Whether Fast fallback algorithm is enabled for socket connect
2657
     * @return the flag
2658
     */
2659
    public boolean isEnableFastFallback() {
2660
        return enableFastFallback;
1✔
2661
    }
2662

2663
    public URI createURIForServer(String serverURI) throws URISyntaxException {
2664
        return new NatsUri(serverURI).getUri();
1✔
2665
    }
2666

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

2680
        appendOption(connectString, Options.OPTION_LANG, Nats.CLIENT_LANGUAGE, true, false);
1✔
2681
        appendOption(connectString, Options.OPTION_VERSION, Nats.CLIENT_VERSION, true, true);
1✔
2682

2683
        if (this.connectionName != null) {
1✔
2684
            appendOption(connectString, Options.OPTION_NAME, this.connectionName, true, true);
1✔
2685
        }
2686

2687
        appendOption(connectString, Options.OPTION_PROTOCOL, "1", false, true);
1✔
2688

2689
        appendOption(connectString, Options.OPTION_VERBOSE, String.valueOf(this.isVerbose()), false, true);
1✔
2690
        appendOption(connectString, Options.OPTION_PEDANTIC, String.valueOf(this.isPedantic()), false, true);
1✔
2691
        appendOption(connectString, Options.OPTION_TLS_REQUIRED, String.valueOf(this.isTLSRequired()), false, true);
1✔
2692
        appendOption(connectString, Options.OPTION_ECHO, String.valueOf(!this.isNoEcho()), false, true);
1✔
2693
        appendOption(connectString, Options.OPTION_HEADERS, String.valueOf(!this.isNoHeaders()), false, true);
1✔
2694
        appendOption(connectString, Options.OPTION_NORESPONDERS, String.valueOf(!this.isNoNoResponders()), false, true);
1✔
2695

2696
        if (includeAuth) {
1✔
2697
            if (nonce != null && this.getAuthHandler() != null) {
1✔
2698
                char[] nkey = this.getAuthHandler().getID();
1✔
2699
                byte[] sig = this.getAuthHandler().sign(nonce);
1✔
2700
                char[] jwt = this.getAuthHandler().getJWT();
1✔
2701

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

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

2710
                if (nkey == null) {
1✔
2711
                    nkey = new char[0];
1✔
2712
                }
2713

2714
                String encodedSig = base64UrlEncodeToString(sig);
1✔
2715

2716
                appendOption(connectString, Options.OPTION_NKEY, nkey, true);
1✔
2717
                appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
1✔
2718
                appendOption(connectString, Options.OPTION_JWT, jwt, true);
1✔
2719
            }
2720

2721
            String uriUser = null;
1✔
2722
            String uriPass = null;
1✔
2723
            String uriToken = null;
1✔
2724

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

2745
            if (uriUser != null) {
1✔
2746
                appendOption(connectString, Options.OPTION_USER, jsonEncode(uriUser), true, true);
1✔
2747
            }
2748
            else if (this.username != null) {
1✔
2749
                appendOption(connectString, Options.OPTION_USER, jsonEncode(this.username), true, true);
1✔
2750
            }
2751

2752
            if (uriPass != null) {
1✔
2753
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(uriPass), true, true);
1✔
2754
            }
2755
            else if (this.password != null) {
1✔
2756
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(this.password), true, true);
1✔
2757
            }
2758

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

2770
        connectString.append("}");
1✔
2771
        connectString.flip();
1✔
2772
        return connectString;
1✔
2773
    }
2774

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

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

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

2802
    private static void _appendOptionEnd(CharBuffer builder, boolean quotes) {
2803
        if (quotes) {
1✔
2804
            builder.append('"');
1✔
2805
        }
2806
    }
1✔
2807

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

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

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

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

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

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

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

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

2909
    private static void classnameProperty(Properties props, String key, java.util.function.Consumer<Object> consumer) {
2910
        stringProperty(props, key, className -> consumer.accept(createInstanceOf(className)));
1✔
2911
    }
1✔
2912

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