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

nats-io / nats.java / #2202

19 Sep 2025 03:44PM UTC coverage: 95.043% (-0.03%) from 95.075%
#2202

push

github

web-flow
Merge pull request #1429 from nats-io/write-timeout-tuning

Ensure write timeout is not less than 100 nanoseconds

7 of 8 new or added lines in 3 files covered. (87.5%)

7 existing lines in 2 files now uncovered.

11964 of 12588 relevant lines covered (95.04%)

0.95 hits per line

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

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

14
package io.nats.client;
15

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

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

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

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

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

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

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

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

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

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

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

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

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

128
    /**
129
     * This is set to 100 nanos to ensure that the scheduled task can execute
130
     */
131
    public static final long MINIMUM_SOCKET_WRITE_TIMEOUT_NANOS = 100;
132

133
    /**
134
     * @deprecated No longer enforcing a minimum
135
     */
136
    @Deprecated
137
    public static final long MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
138

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

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

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

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

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

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

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

191
    /**
192
     * Default size for buffers in the connection, not as available as other settings,
193
     * this is primarily changed for testing, {@link #getBufferSize() getBufferSize()}.
194
     */
195
    public static final int DEFAULT_BUFFER_SIZE = 64 * 1024;
196

197
    /**
198
     * Default thread name prefix. Used by the default executor when creating threads.
199
     * This property is defined as {@value}
200
     */
201
    public static final String DEFAULT_THREAD_NAME_PREFIX = "nats";
202

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

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

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

229
    /**
230
     * This value is used internally to discard messages when the outgoing queue is full.
231
     * See {@link #DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE}
232
     */
233
    public static final boolean DEFAULT_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL = false;
234

235
    /**
236
     * Default supplier for creating a single-threaded executor service.
237
     */
238
    public static final Supplier<ExecutorService> DEFAULT_SINGLE_THREAD_EXECUTOR = Executors::newSingleThreadExecutor;
1✔
239

240
    // ----------------------------------------------------------------------------------------------------
241
    // ENVIRONMENT PROPERTIES
242
    // ----------------------------------------------------------------------------------------------------
243
    static final String PFX = "io.nats.client.";
244
    static final int PFX_LEN = PFX.length();
1✔
245

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

540
    /**
541
     * Property used to enable fast fallback algorithm for socket connection.
542
     * {@link Builder#enableFastFallback() enableFastFallback}.
543
     */
544
    public static final String PROP_FAST_FALLBACK = PFX + "fast.fallback";
545

546
    // ----------------------------------------------------------------------------------------------------
547
    // PROTOCOL CONNECT OPTION CONSTANTS
548
    // ----------------------------------------------------------------------------------------------------
549
    /**
550
     * Protocol key {@value}, see {@link Builder#verbose() verbose}.
551
     */
552
    static final String OPTION_VERBOSE = "verbose";
553

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

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

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

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

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

584
    /**
585
     * Protocol key {@value}, see {@link Builder#connectionName(String)
586
     * connectionName}.
587
     */
588
    static final String OPTION_NAME = "name";
589

590
    /**
591
     * Protocol key {@value}, will be set to "Java".
592
     */
593
    static final String OPTION_LANG = "lang";
594

595
    /**
596
     * Protocol key {@value}, will be set to
597
     * {@link Nats#CLIENT_VERSION CLIENT_VERSION}.
598
     */
599
    static final String OPTION_VERSION = "version";
600

601
    /**
602
     * Protocol key {@value}, will be set to 1.
603
     */
604
    static final String OPTION_PROTOCOL = "protocol";
605

606
    /**
607
     * Echo key {@value}, determines if the server should echo to the client.
608
     */
609
    static final String OPTION_ECHO = "echo";
610

611
    /**
612
     * NKey key {@value}, the public key being used for sign-in.
613
     */
614
    static final String OPTION_NKEY = "nkey";
615

616
    /**
617
     * SIG key {@value}, the signature of the nonce sent by the server.
618
     */
619
    static final String OPTION_SIG = "sig";
620

621
    /**
622
     * JWT key {@value}, the user JWT to send to the server.
623
     */
624
    static final String OPTION_JWT = "jwt";
625

626
    /**
627
     * Headers key if headers are supported
628
     */
629
    static final String OPTION_HEADERS = "headers";
630

631
    /**
632
     * No Responders key if noresponders are supported
633
     */
634
    static final String OPTION_NORESPONDERS = "no_responders";
635

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

680
    private final AuthHandler authHandler;
681
    private final ReconnectDelayHandler reconnectDelayHandler;
682

683
    private final ErrorListener errorListener;
684
    private final TimeTraceLogger timeTraceLogger;
685
    private final ConnectionListener connectionListener;
686
    private final ReadListener readListener;
687
    private final StatisticsCollector statisticsCollector;
688
    private final String dataPortType;
689

690
    private final boolean trackAdvancedStats;
691
    private final boolean traceConnection;
692

693
    private final ExecutorService executor;
694
    private final ScheduledExecutorService scheduledExecutor;
695
    private final ThreadFactory connectThreadFactory;
696
    private final ThreadFactory callbackThreadFactory;
697
    private final ServerPool serverPool;
698
    private final DispatcherFactory dispatcherFactory;
699

700
    private final List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
701
    private final Proxy proxy;
702
    private final boolean enableFastFallback;
703

704
    static class DefaultThreadFactory implements ThreadFactory {
705
        final String name;
706
        final AtomicInteger threadNo;
707

708
        public DefaultThreadFactory (String name){
1✔
709
            this.name = name;
1✔
710
            threadNo = new AtomicInteger(0);
1✔
711
        }
1✔
712

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

726
    static class DefaultTokenSupplier implements Supplier<char[]> {
727
        final char[] token;
728

729
        public DefaultTokenSupplier() {
1✔
730
            token = null;
1✔
731
        }
1✔
732

733
        public DefaultTokenSupplier(char[] token) {
1✔
734
            this.token = token == null || token.length == 0 ? null : token;
1✔
735
        }
1✔
736

737
        public DefaultTokenSupplier(String token) {
1✔
738
            token = Validator.emptyAsNull(token);
1✔
739
            this.token = token == null ? null : token.toCharArray();
1✔
740
        }
1✔
741

742
        @Override
743
        public char[] get() {
744
            return token;
1✔
745
        }
746
    }
747

748
    /**
749
     * Set old request style.
750
     * @param value true to use the old request style
751
     * @deprecated Use Builder
752
     */
753
    @Deprecated
754
    public void setOldRequestStyle(boolean value) {
755
        useOldRequestStyle = value;
1✔
756
    }
1✔
757

758
    // ----------------------------------------------------------------------------------------------------
759
    // BUILDER
760
    // ----------------------------------------------------------------------------------------------------
761
    /**
762
     * Creates a builder for the options in a fluent style
763
     * @return the builder.
764
     */
765
    public static Builder builder() {
766
        return new Builder();
1✔
767
    }
768

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

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

827
        private AuthHandler authHandler;
828
        private ReconnectDelayHandler reconnectDelayHandler;
829

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

843
        private boolean useDefaultTls;
844
        private boolean useTrustAllTls;
845
        private String keystore;
846
        private char[] keystorePassword;
847
        private String truststore;
848
        private char[] truststorePassword;
849
        private String tlsAlgorithm = DEFAULT_TLS_ALGORITHM;
1✔
850
        private String credentialPath;
851
        private boolean enableFastFallback = false;
1✔
852

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

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

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

883
        // ----------------------------------------------------------------------------------------------------
884
        // BUILDER METHODS
885
        // ----------------------------------------------------------------------------------------------------
886

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

903
            charArrayProperty(props, PROP_USERNAME, ca -> this.username = ca);
1✔
904
            charArrayProperty(props, PROP_PASSWORD, ca -> this.password = ca);
1✔
905
            charArrayProperty(props, PROP_TOKEN, ca -> this.tokenSupplier = new DefaultTokenSupplier(ca));
1✔
906
            //noinspection unchecked
907
            classnameProperty(props, PROP_TOKEN_SUPPLIER, o -> this.tokenSupplier = (Supplier<char[]>) o);
1✔
908

909
            booleanProperty(props, PROP_SECURE, b -> this.useDefaultTls = b);
1✔
910
            booleanProperty(props, PROP_OPENTLS, b -> this.useTrustAllTls = b);
1✔
911

912
            classnameProperty(props, PROP_SSL_CONTEXT_FACTORY_CLASS, o -> this.sslContextFactory = (SSLContextFactory) o);
1✔
913
            stringProperty(props, PROP_KEYSTORE, s -> this.keystore = s);
1✔
914
            charArrayProperty(props, PROP_KEYSTORE_PASSWORD, ca -> this.keystorePassword = ca);
1✔
915
            stringProperty(props, PROP_TRUSTSTORE, s -> this.truststore = s);
1✔
916
            charArrayProperty(props, PROP_TRUSTSTORE_PASSWORD, ca -> this.truststorePassword = ca);
1✔
917
            stringProperty(props, PROP_TLS_ALGORITHM, s -> this.tlsAlgorithm = s);
1✔
918

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

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

923
            booleanProperty(props, PROP_NORANDOMIZE, b -> this.noRandomize = b);
1✔
924
            booleanProperty(props, PROP_NO_RESOLVE_HOSTNAMES, b -> this.noResolveHostnames = b);
1✔
925
            booleanProperty(props, PROP_REPORT_NO_RESPONDERS, b -> this.reportNoResponders = b);
1✔
926

927
            stringProperty(props, PROP_CONNECTION_NAME, s -> this.connectionName = s);
1✔
928
            booleanProperty(props, PROP_VERBOSE, b -> this.verbose = b);
1✔
929
            booleanProperty(props, PROP_NO_ECHO, b -> this.noEcho = b);
1✔
930
            booleanProperty(props, PROP_NO_HEADERS, b -> this.noHeaders = b);
1✔
931
            booleanProperty(props, PROP_NO_NORESPONDERS, b -> this.noNoResponders = b);
1✔
932
            booleanProperty(props, PROP_CLIENT_SIDE_LIMIT_CHECKS, b -> this.clientSideLimitChecks = b);
1✔
933
            booleanProperty(props, PROP_UTF8_SUBJECTS, b -> this.supportUTF8Subjects = b);
1✔
934
            booleanProperty(props, PROP_PEDANTIC, b -> this.pedantic = b);
1✔
935

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

946
            intGtEqZeroProperty(props, PROP_MAX_CONTROL_LINE, DEFAULT_MAX_CONTROL_LINE, i -> this.maxControlLine = i);
1✔
947
            durationProperty(props, PROP_PING_INTERVAL, DEFAULT_PING_INTERVAL, d -> this.pingInterval = d);
1✔
948
            durationProperty(props, PROP_CLEANUP_INTERVAL, DEFAULT_REQUEST_CLEANUP_INTERVAL, d -> this.requestCleanupInterval = d);
1✔
949
            intProperty(props, PROP_MAX_PINGS, DEFAULT_MAX_PINGS_OUT, i -> this.maxPingsOut = i);
1✔
950
            booleanProperty(props, PROP_USE_OLD_REQUEST_STYLE, b -> this.useOldRequestStyle = b);
1✔
951

952
            classnameProperty(props, PROP_ERROR_LISTENER, o -> this.errorListener = (ErrorListener) o);
1✔
953
            classnameProperty(props, PROP_TIME_TRACE_LOGGER, o -> this.timeTraceLogger = (TimeTraceLogger) o);
1✔
954
            classnameProperty(props, PROP_CONNECTION_CB, o -> this.connectionListener = (ConnectionListener) o);
1✔
955
            classnameProperty(props, PROP_READ_LISTENER_CLASS, o -> this.readListener = (ReadListener) o);
1✔
956
            classnameProperty(props, PROP_STATISTICS_COLLECTOR, o -> this.statisticsCollector = (StatisticsCollector) o);
1✔
957

958
            stringProperty(props, PROP_DATA_PORT_TYPE, s -> this.dataPortType = s);
1✔
959
            stringProperty(props, PROP_INBOX_PREFIX, this::inboxPrefix);
1✔
960
            intGtEqZeroProperty(props, PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE, DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE, i -> this.maxMessagesInOutgoingQueue = i);
1✔
961
            booleanProperty(props, PROP_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL, b -> this.discardMessagesWhenOutgoingQueueFull = b);
1✔
962

963
            booleanProperty(props, PROP_IGNORE_DISCOVERED_SERVERS, b -> this.ignoreDiscoveredServers = b);
1✔
964
            booleanProperty(props, PROP_TLS_FIRST, b -> this.tlsFirst = b);
1✔
965
            booleanProperty(props, PROP_USE_TIMEOUT_EXCEPTION, b -> this.useTimeoutException = b);
1✔
966
            booleanProperty(props, PROP_USE_DISPATCHER_WITH_EXECUTOR, b -> this.useDispatcherWithExecutor = b);
1✔
967
            booleanProperty(props, PROP_FORCE_FLUSH_ON_REQUEST, b -> this.forceFlushOnRequest = b);
1✔
968
            booleanProperty(props, PROP_FAST_FALLBACK, b -> this.enableFastFallback = b);
1✔
969

970
            classnameProperty(props, PROP_SERVERS_POOL_IMPLEMENTATION_CLASS, o -> this.serverPool = (ServerPool) o);
1✔
971
            classnameProperty(props, PROP_DISPATCHER_FACTORY_CLASS, o -> this.dispatcherFactory = (DispatcherFactory) o);
1✔
972
            classnameProperty(props, PROP_EXECUTOR_SERVICE_CLASS, o -> this.executor = (ExecutorService) o);
1✔
973
            classnameProperty(props, PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS, o -> this.scheduledExecutor = (ScheduledExecutorService) o);
1✔
974
            classnameProperty(props, PROP_CONNECT_THREAD_FACTORY_CLASS, o -> this.connectThreadFactory = (ThreadFactory) o);
1✔
975
            classnameProperty(props, PROP_CALLBACK_THREAD_FACTORY_CLASS, o -> this.callbackThreadFactory = (ThreadFactory) o);
1✔
976
            return this;
1✔
977
        }
978

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

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

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

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

1039
        /**
1040
         * For the default server list provider, whether to resolve hostnames when building server list.
1041
         * @return the Builder for chaining
1042
         */
1043
        public Builder noResolveHostnames() {
1044
            this.noResolveHostnames = true;
1✔
1045
            return this;
1✔
1046
        }
1047

1048
        public Builder reportNoResponders() {
1049
            this.reportNoResponders = true;
1✔
1050
            return this;
1✔
1051
        }
1052

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

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

1074
        /**
1075
         * Turn off noresponder support. Some versions of the server don't support it.
1076
         * @return the Builder for chaining
1077
         */
1078
        public Builder noNoResponders() {
1079
            this.noNoResponders = true;
1✔
1080
            return this;
1✔
1081
        }
1082

1083
        /**
1084
         * Set client side limit checks. Default is true
1085
         * @param checks the checks flag
1086
         * @return the Builder for chaining
1087
         */
1088
        public Builder clientSideLimitChecks(boolean checks) {
1089
            this.clientSideLimitChecks = checks;
1✔
1090
            return this;
1✔
1091
        }
1092

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

1105
        /**
1106
         * Set the connection's optional Name.
1107
         *
1108
         * @param name the connections new name.
1109
         * @return the Builder for chaining
1110
         */
1111
        public Builder connectionName(String name) {
1112
            this.connectionName = name;
1✔
1113
            return this;
1✔
1114
        }
1115

1116
        /**
1117
         * Set the connection's inbox prefix. All inboxes will start with this string.
1118
         *
1119
         * @param prefix prefix to use.
1120
         * @return the Builder for chaining
1121
         */
1122
        public Builder inboxPrefix(String prefix) {
1123
            this.inboxPrefix = prefix;
1✔
1124

1125
            if (!this.inboxPrefix.endsWith(".")) {
1✔
1126
                this.inboxPrefix = this.inboxPrefix + ".";
1✔
1127
            }
1128
            return this;
1✔
1129
        }
1130

1131
        /**
1132
         * Turn on verbose mode with the server.
1133
         * @return the Builder for chaining
1134
         */
1135
        public Builder verbose() {
1136
            this.verbose = true;
1✔
1137
            return this;
1✔
1138
        }
1139

1140
        /**
1141
         * Turn on pedantic mode for the server, in relation to this connection.
1142
         * @return the Builder for chaining
1143
         */
1144
        public Builder pedantic() {
1145
            this.pedantic = true;
1✔
1146
            return this;
1✔
1147
        }
1148

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

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

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

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

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

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

1213
        /**
1214
         *
1215
         * @param keystore the path to the keystore file
1216
         * @return the Builder for chaining
1217
         */
1218
        public Builder keystorePath(String keystore) {
1219
            this.keystore = emptyAsNull(keystore);
1✔
1220
            return this;
1✔
1221
        }
1222

1223
        /**
1224
         *
1225
         * @param keystorePassword the password for the keystore
1226
         * @return the Builder for chaining
1227
         */
1228
        public Builder keystorePassword(char[] keystorePassword) {
1229
            this.keystorePassword = keystorePassword == null || keystorePassword.length == 0 ? null : keystorePassword;
1✔
1230
            return this;
1✔
1231
        }
1232

1233
        /**
1234
         *
1235
         * @param truststore the path to the trust store file
1236
         * @return the Builder for chaining
1237
         */
1238
        public Builder truststorePath(String truststore) {
1239
            this.truststore = emptyAsNull(truststore);
1✔
1240
            return this;
1✔
1241
        }
1242

1243
        /**
1244
         *
1245
         * @param truststorePassword the password for the trust store
1246
         * @return the Builder for chaining
1247
         */
1248
        public Builder truststorePassword(char[] truststorePassword) {
1249
            this.truststorePassword = truststorePassword == null || truststorePassword.length == 0 ? null : truststorePassword;
1✔
1250
            return this;
1✔
1251
        }
1252

1253
        /**
1254
         *
1255
         * @param tlsAlgorithm the tls algorithm. Default is {@value SSLUtils#DEFAULT_TLS_ALGORITHM}
1256
         * @return the Builder for chaining
1257
         */
1258
        public Builder tlsAlgorithm(String tlsAlgorithm) {
1259
            this.tlsAlgorithm = emptyOrNullAs(tlsAlgorithm, DEFAULT_TLS_ALGORITHM);
1✔
1260
            return this;
1✔
1261
        }
1262

1263
        /**
1264
         *
1265
         * @param credentialPath the path to the credentials file for creating an {@link AuthHandler AuthHandler}
1266
         * @return the Builder for chaining
1267
         */
1268
        public Builder credentialPath(String credentialPath) {
1269
            this.credentialPath = emptyAsNull(credentialPath);
1✔
1270
            return this;
1✔
1271
        }
1272

1273
        /**
1274
         * Equivalent to calling maxReconnects with 0, {@link #maxReconnects(int) maxReconnects}.
1275
         * @return the Builder for chaining
1276
         */
1277
        public Builder noReconnect() {
1278
            this.maxReconnect = 0;
1✔
1279
            return this;
1✔
1280
        }
1281

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

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

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

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

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

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

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

1382
        /**
1383
         * Set the timeout to use around socket reads
1384
         * @param socketReadTimeoutMillis the timeout milliseconds
1385
         * @return the Builder for chaining
1386
         */
1387
        public Builder socketReadTimeoutMillis(int socketReadTimeoutMillis) {
1388
            this.socketReadTimeoutMillis = socketReadTimeoutMillis;
×
1389
            return this;
×
1390
        }
1391

1392
        /**
1393
         * Set the timeout to use around socket writes
1394
         * @param socketWriteTimeoutMillis the timeout milliseconds
1395
         * @return the Builder for chaining
1396
         */
1397
        public Builder socketWriteTimeout(long socketWriteTimeoutMillis) {
1398
            socketWriteTimeout = Duration.ofMillis(socketWriteTimeoutMillis);
1✔
1399
            return this;
1✔
1400
        }
1401

1402
        /**
1403
         * Set the timeout to use around socket writes
1404
         * @param socketWriteTimeout the timeout duration
1405
         * @return the Builder for chaining
1406
         */
1407
        public Builder socketWriteTimeout(Duration socketWriteTimeout) {
1408
            this.socketWriteTimeout = socketWriteTimeout;
1✔
1409
            return this;
1✔
1410
        }
1411

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

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

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

1461
        /**
1462
         * Set the maximum number of pings the client can have in flight.
1463
         *
1464
         * @param max the max pings
1465
         * @return the Builder for chaining
1466
         */
1467
        public Builder maxPingsOut(int max) {
1468
            this.maxPingsOut = max;
1✔
1469
            return this;
1✔
1470
        }
1471

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

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

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

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

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

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

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

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

1579
        /**
1580
         * Set the {@link ReconnectDelayHandler ReconnectDelayHandler} for custom reconnect duration
1581
         *
1582
         * @param handler The new ReconnectDelayHandler for this connection.
1583
         * @return the Builder for chaining
1584
         */
1585
        public Builder reconnectDelayHandler(ReconnectDelayHandler handler) {
1586
            this.reconnectDelayHandler = handler;
1✔
1587
            return this;
1✔
1588
        }
1589

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

1602
        /**
1603
         * Set the {@link TimeTraceLogger TimeTraceLogger} to receive trace events related to this connection.
1604
         * @param logger The new TimeTraceLogger for this connection.
1605
         * @return the Builder for chaining
1606
         */
1607
        public Builder timeTraceLogger(TimeTraceLogger logger) {
1608
            this.timeTraceLogger = logger;
1✔
1609
            return this;
1✔
1610
        }
1611

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

1624
        /**
1625
         * Sets a listener to be notified on incoming protocol/message
1626
         *
1627
         * @param readListener the listener
1628
         * @return the Builder for chaining
1629
         */
1630
        public Builder readListener(ReadListener readListener) {
1631
            this.readListener = readListener;
×
1632
            return this;
×
1633
        }
1634

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

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

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

1678
        /**
1679
         * Sets custom thread factory for the executor service
1680
         *
1681
         * @param threadFactory the thread factory to use for the executor service
1682
         * @return the Builder for chaining
1683
         */
1684
        public Builder connectThreadFactory(ThreadFactory threadFactory) {
1685
            this.connectThreadFactory = threadFactory;
1✔
1686
            return this;
1✔
1687
        }
1688

1689
        /**
1690
         * Sets custom thread factory for the executor service
1691
         *
1692
         * @param threadFactory the thread factory to use for the executor service
1693
         * @return the Builder for chaining
1694
         */
1695
        public Builder callbackThreadFactory(ThreadFactory threadFactory) {
1696
            this.callbackThreadFactory = threadFactory;
1✔
1697
            return this;
1✔
1698
        }
1699

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

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

1725
        /**
1726
         * Define a proxy to use when connecting.
1727
         *
1728
         * @param proxy is the HTTP or socks proxy to use.
1729
         * @return the Builder for chaining
1730
         */
1731
        public Builder proxy(Proxy proxy) {
1732
            this.proxy = proxy;
1✔
1733
            return this;
1✔
1734
        }
1735

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

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

1761
        /**
1762
         * Enable discard messages when the outgoing queue full. See {@link Builder#maxMessagesInOutgoingQueue(int) maxMessagesInOutgoingQueue}
1763
         *
1764
         * @return the Builder for chaining
1765
         */
1766
        public Builder discardMessagesWhenOutgoingQueueFull() {
1767
            this.discardMessagesWhenOutgoingQueueFull = true;
1✔
1768
            return this;
1✔
1769
        }
1770

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

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

1794
        /**
1795
         * Throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}?
1796
         * @return the Builder for chaining
1797
         */
1798
        public Builder useTimeoutException() {
1799
            this.useTimeoutException = true;
×
1800
            return this;
×
1801
        }
1802

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

1812
        /**
1813
         * Instruct requests to turn off flush on requests.
1814
         * @return the Builder for chaining
1815
         */
1816
        public Builder dontForceFlushOnRequest() {
1817
            this.forceFlushOnRequest = false;
×
1818
            return this;
×
1819
        }
1820

1821
        /**
1822
         * Set the ServerPool implementation for connections to use instead of the default implementation
1823
         * @param serverPool the implementation
1824
         * @return the Builder for chaining
1825
         */
1826
        public Builder serverPool(ServerPool serverPool) {
1827
            this.serverPool = serverPool;
1✔
1828
            return this;
1✔
1829
        }
1830

1831
        /**
1832
         * Set the DispatcherFactory implementation for connections to use instead of the default implementation
1833
         * @param dispatcherFactory the implementation
1834
         * @return the Builder for chaining
1835
         */
1836
        public Builder dispatcherFactory(DispatcherFactory dispatcherFactory) {
1837
            this.dispatcherFactory = dispatcherFactory;
×
1838
            return this;
×
1839
        }
1840

1841
        /**
1842
         * Whether to enable Fast fallback algorithm for socket connect
1843
         * @return the Builder for chaining
1844
         */
1845
        public Builder enableFastFallback() {
1846
            this.enableFastFallback = true;
1✔
1847
            return this;
1✔
1848
        }
1849

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

1876
            if (inboxPrefix == null) {
1✔
1877
                inboxPrefix = DEFAULT_INBOX_PREFIX;
×
1878
            }
1879

1880
            boolean checkUrisForSecure = true;
1✔
1881
            if (natsServerUris.isEmpty()) {
1✔
1882
                server(DEFAULT_URL);
1✔
1883
                checkUrisForSecure = false;
1✔
1884
            }
1885

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

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

1950
            if (tlsFirst && sslContext == null) {
1✔
1951
                throw new IllegalStateException("SSL context required for tls handshake first");
×
1952
            }
1953

1954
            if (credentialPath != null) {
1✔
1955
                File file = new File(credentialPath).getAbsoluteFile();
1✔
1956
                authHandler = Nats.credentials(file.toString());
1✔
1957
            }
1958

1959
            if (socketReadTimeoutMillis < 1) {
1✔
1960
                socketReadTimeoutMillis = 0; // just for consistency. The connection compares to gt 0
1✔
1961
            }
1962

1963
            if (socketWriteTimeout != null && socketWriteTimeout.toNanos() < MINIMUM_SOCKET_WRITE_TIMEOUT_NANOS) {
1✔
NEW
1964
                throw new IllegalArgumentException("Socket Write Timeout cannot be less than " + MINIMUM_SOCKET_WRITE_TIMEOUT_NANOS + " nanoseconds.");
×
1965
            }
1966

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

© 2025 Coveralls, Inc