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

nats-io / nats.java / #2020

29 Jun 2025 06:09PM UTC coverage: 95.643% (+0.05%) from 95.598%
#2020

push

github

web-flow
Merge pull request #1335 from nats-io/replace-timer

Replace Timer with scheduled tasks

66 of 77 new or added lines in 6 files covered. (85.71%)

1 existing line in 1 file now uncovered.

11767 of 12303 relevant lines covered (95.64%)

0.96 hits per line

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

96.46
/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

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

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

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

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

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

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

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

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

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

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

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

121
    /**
122
     * Constant used for calculating if a socket write timeout is large enough.
123
     */
124
    public static final long MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
125

126
    /**
127
     * Constant used for calculating if a socket read timeout is large enough.
128
     */
129
    public static final long MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT = 100;
130

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

528
    // ----------------------------------------------------------------------------------------------------
529
    // PROTOCOL CONNECT OPTION CONSTANTS
530
    // ----------------------------------------------------------------------------------------------------
531
    /**
532
     * Protocol key {@value}, see {@link Builder#verbose() verbose}.
533
     */
534
    static final String OPTION_VERBOSE = "verbose";
535

536
    /**
537
     * Protocol key {@value}, see {@link Builder#pedantic()
538
     * pedantic}.
539
     */
540
    static final String OPTION_PEDANTIC = "pedantic";
541

542
    /**
543
     * Protocol key {@value}, see
544
     * {@link Builder#sslContext(SSLContext) sslContext}.
545
     */
546
    static final String OPTION_TLS_REQUIRED = "tls_required";
547

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

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

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

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

572
    /**
573
     * Protocol key {@value}, will be set to "Java".
574
     */
575
    static final String OPTION_LANG = "lang";
576

577
    /**
578
     * Protocol key {@value}, will be set to
579
     * {@link Nats#CLIENT_VERSION CLIENT_VERSION}.
580
     */
581
    static final String OPTION_VERSION = "version";
582

583
    /**
584
     * Protocol key {@value}, will be set to 1.
585
     */
586
    static final String OPTION_PROTOCOL = "protocol";
587

588
    /**
589
     * Echo key {@value}, determines if the server should echo to the client.
590
     */
591
    static final String OPTION_ECHO = "echo";
592

593
    /**
594
     * NKey key {@value}, the public key being used for sign-in.
595
     */
596
    static final String OPTION_NKEY = "nkey";
597

598
    /**
599
     * SIG key {@value}, the signature of the nonce sent by the server.
600
     */
601
    static final String OPTION_SIG = "sig";
602

603
    /**
604
     * JWT key {@value}, the user JWT to send to the server.
605
     */
606
    static final String OPTION_JWT = "jwt";
607

608
    /**
609
     * Headers key if headers are supported
610
     */
611
    static final String OPTION_HEADERS = "headers";
612

613
    /**
614
     * No Responders key if noresponders are supported
615
     */
616
    static final String OPTION_NORESPONDERS = "no_responders";
617

618
    // ----------------------------------------------------------------------------------------------------
619
    // CLASS VARIABLES
620
    // ----------------------------------------------------------------------------------------------------
621
    private final List<NatsUri> natsServerUris;
622
    private final List<String> unprocessedServers;
623
    private final boolean noRandomize;
624
    private final boolean noResolveHostnames;
625
    private final boolean reportNoResponders;
626
    private final String connectionName;
627
    private final boolean verbose;
628
    private final boolean pedantic;
629
    private final SSLContext sslContext;
630
    private final int maxReconnect;
631
    private final int maxControlLine;
632
    private final Duration reconnectWait;
633
    private final Duration reconnectJitter;
634
    private final Duration reconnectJitterTls;
635
    private final Duration connectionTimeout;
636
    private final int socketReadTimeoutMillis;
637
    private final Duration socketWriteTimeout;
638
    private final int socketSoLinger;
639
    private final Duration pingInterval;
640
    private final Duration requestCleanupInterval;
641
    private final int maxPingsOut;
642
    private final long reconnectBufferSize;
643
    private final char[] username;
644
    private final char[] password;
645
    private final Supplier<char[]> tokenSupplier;
646
    private final String inboxPrefix;
647
    private boolean useOldRequestStyle;
648
    private final int bufferSize;
649
    private final boolean noEcho;
650
    private final boolean noHeaders;
651
    private final boolean noNoResponders;
652
    private final boolean clientSideLimitChecks;
653
    private final boolean supportUTF8Subjects;
654
    private final int maxMessagesInOutgoingQueue;
655
    private final boolean discardMessagesWhenOutgoingQueueFull;
656
    private final boolean ignoreDiscoveredServers;
657
    private final boolean tlsFirst;
658
    private final boolean useTimeoutException;
659
    private final boolean useDispatcherWithExecutor;
660
    private final boolean forceFlushOnRequest;
661

662
    private final AuthHandler authHandler;
663
    private final ReconnectDelayHandler reconnectDelayHandler;
664

665
    private final ErrorListener errorListener;
666
    private final TimeTraceLogger timeTraceLogger;
667
    private final ConnectionListener connectionListener;
668
    private final ReadListener readListener;
669
    private final StatisticsCollector statisticsCollector;
670
    private final String dataPortType;
671

672
    private final boolean trackAdvancedStats;
673
    private final boolean traceConnection;
674

675
    private final ExecutorService executor;
676
    private final ScheduledExecutorService scheduledExecutor;
677
    private final ThreadFactory connectThreadFactory;
678
    private final ThreadFactory callbackThreadFactory;
679
    private final ServerPool serverPool;
680
    private final DispatcherFactory dispatcherFactory;
681

682
    private final List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
683
    private final Proxy proxy;
684

685
    static class DefaultThreadFactory implements ThreadFactory {
686
        String name;
687
        AtomicInteger threadNo = new AtomicInteger(0);
1✔
688

689
        public DefaultThreadFactory (String name){
1✔
690
            this.name = name;
1✔
691
        }
1✔
692

693
        public Thread newThread(Runnable r) {
694
            String threadName = name+":"+threadNo.incrementAndGet();
1✔
695
            Thread t = new Thread(r,threadName);
1✔
696
            if (t.isDaemon()) {
1✔
697
                t.setDaemon(false);
×
698
            }
699
            if (t.getPriority() != Thread.NORM_PRIORITY) {
1✔
700
                t.setPriority(Thread.NORM_PRIORITY);
×
701
            }
702
            return t;
1✔
703
        }
704
    }
705

706
    static class DefaultTokenSupplier implements Supplier<char[]> {
707
        final char[] token;
708

709
        public DefaultTokenSupplier() {
1✔
710
            token = null;
1✔
711
        }
1✔
712

713
        public DefaultTokenSupplier(char[] token) {
1✔
714
            this.token = token == null || token.length == 0 ? null : token;
1✔
715
        }
1✔
716

717
        public DefaultTokenSupplier(String token) {
1✔
718
            token = Validator.emptyAsNull(token);
1✔
719
            this.token = token == null ? null : token.toCharArray();
1✔
720
        }
1✔
721

722
        @Override
723
        public char[] get() {
724
            return token;
1✔
725
        }
726
    }
727

728
    /**
729
     * Set old request style.
730
     * @param value true to use the old request style
731
     * @deprecated Use Builder
732
     */
733
    @Deprecated
734
    public void setOldRequestStyle(boolean value) {
735
        useOldRequestStyle = value;
1✔
736
    }
1✔
737

738
    // ----------------------------------------------------------------------------------------------------
739
    // BUILDER
740
    // ----------------------------------------------------------------------------------------------------
741
    /**
742
     * Creates a builder for the options in a fluent style
743
     * @return the builder.
744
     */
745
    public static Builder builder() {
746
        return new Builder();
1✔
747
    }
748

749
    /**
750
     * Options are created using a Builder. The builder supports chaining and will
751
     * create a default set of options if no methods are calls. The builder can also
752
     * be created from a properties object using the property names defined with the
753
     * prefix PROP_ in this class.
754
     * <p>A common usage for testing might be {@code new Options.Builder().server(myserverurl).noReconnect.build()}
755
     */
756
    public static class Builder {
757

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

807
        private AuthHandler authHandler;
808
        private ReconnectDelayHandler reconnectDelayHandler;
809

810
        private ErrorListener errorListener = null;
1✔
811
        private TimeTraceLogger timeTraceLogger = null;
1✔
812
        private ConnectionListener connectionListener = null;
1✔
813
        private ReadListener readListener = null;
1✔
814
        private StatisticsCollector statisticsCollector = null;
1✔
815
        private String dataPortType = DEFAULT_DATA_PORT_TYPE;
1✔
816
        private ExecutorService executor;
817
        private ScheduledExecutorService scheduledExecutor;
818
        private ThreadFactory connectThreadFactory;
819
        private ThreadFactory callbackThreadFactory;
820
        private List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
821
        private Proxy proxy;
822

823
        private boolean useDefaultTls;
824
        private boolean useTrustAllTls;
825
        private String keystore;
826
        private char[] keystorePassword;
827
        private String truststore;
828
        private char[] truststorePassword;
829
        private String tlsAlgorithm = DEFAULT_TLS_ALGORITHM;
1✔
830
        private String credentialPath;
831

832
        /**
833
         * Constructs a new Builder with the default values.
834
         * <p>When {@link #build() build()} is called on a default builder it will add the {@link Options#DEFAULT_URL
835
         * default url} to its list of servers if there were no servers defined.</p>
836
         */
837
        public Builder() {}
1✔
838

839
        // ----------------------------------------------------------------------------------------------------
840
        // BUILD CONSTRUCTOR PROPS
841
        // ----------------------------------------------------------------------------------------------------
842
        /**
843
         * Constructs a new {@code Builder} from a {@link Properties} object.
844
         * <p>Methods called on the builder after construction can override the properties.</p>
845
         * @param props the {@link Properties} object
846
         */
847
        public Builder(Properties props) throws IllegalArgumentException {
1✔
848
            properties(props);
1✔
849
        }
1✔
850

851
        /**
852
         * Constructs a new {@code Builder} from a file that contains properties.
853
         * @param propertiesFilePath a resolvable path to a file from the location the application is running, either relative or absolute
854
         * @throws IOException if the properties file cannot be found, opened or read
855
         */
856
        public Builder(String propertiesFilePath) throws IOException {
1✔
857
            Properties props = new Properties();
1✔
858
            props.load(Files.newInputStream(Paths.get(propertiesFilePath)));
1✔
859
            properties(props);
1✔
860
        }
1✔
861

862
        // ----------------------------------------------------------------------------------------------------
863
        // BUILDER METHODS
864
        // ----------------------------------------------------------------------------------------------------
865

866
        /**
867
         * Add settings defined in the properties object
868
         * @param props the properties object
869
         * @throws IllegalArgumentException if the properties object is null
870
         * @return the Builder for chaining
871
         */
872
        public Builder properties(Properties props) {
873
            if (props == null) {
1✔
874
                throw new IllegalArgumentException("Properties cannot be null");
1✔
875
            }
876
            stringProperty(props, PROP_URL, this::server);
1✔
877
            stringProperty(props, PROP_SERVERS, str -> {
1✔
878
                String[] servers = str.trim().split(",\\s*");
1✔
879
                this.servers(servers);
1✔
880
            });
1✔
881

882
            charArrayProperty(props, PROP_USERNAME, ca -> this.username = ca);
1✔
883
            charArrayProperty(props, PROP_PASSWORD, ca -> this.password = ca);
1✔
884
            charArrayProperty(props, PROP_TOKEN, ca -> this.tokenSupplier = new DefaultTokenSupplier(ca));
1✔
885

886
            booleanProperty(props, PROP_SECURE, b -> this.useDefaultTls = b);
1✔
887
            booleanProperty(props, PROP_OPENTLS, b -> this.useTrustAllTls = b);
1✔
888

889
            classnameProperty(props, PROP_SSL_CONTEXT_FACTORY_CLASS, o -> this.sslContextFactory = (SSLContextFactory) o);
1✔
890
            stringProperty(props, PROP_KEYSTORE, s -> this.keystore = s);
1✔
891
            charArrayProperty(props, PROP_KEYSTORE_PASSWORD, ca -> this.keystorePassword = ca);
1✔
892
            stringProperty(props, PROP_TRUSTSTORE, s -> this.truststore = s);
1✔
893
            charArrayProperty(props, PROP_TRUSTSTORE_PASSWORD, ca -> this.truststorePassword = ca);
1✔
894
            stringProperty(props, PROP_TLS_ALGORITHM, s -> this.tlsAlgorithm = s);
1✔
895

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

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

900
            booleanProperty(props, PROP_NORANDOMIZE, b -> this.noRandomize = b);
1✔
901
            booleanProperty(props, PROP_NO_RESOLVE_HOSTNAMES, b -> this.noResolveHostnames = b);
1✔
902
            booleanProperty(props, PROP_REPORT_NO_RESPONDERS, b -> this.reportNoResponders = b);
1✔
903

904
            stringProperty(props, PROP_CONNECTION_NAME, s -> this.connectionName = s);
1✔
905
            booleanProperty(props, PROP_VERBOSE, b -> this.verbose = b);
1✔
906
            booleanProperty(props, PROP_NO_ECHO, b -> this.noEcho = b);
1✔
907
            booleanProperty(props, PROP_NO_HEADERS, b -> this.noHeaders = b);
1✔
908
            booleanProperty(props, PROP_NO_NORESPONDERS, b -> this.noNoResponders = b);
1✔
909
            booleanProperty(props, PROP_CLIENT_SIDE_LIMIT_CHECKS, b -> this.clientSideLimitChecks = b);
1✔
910
            booleanProperty(props, PROP_UTF8_SUBJECTS, b -> this.supportUTF8Subjects = b);
1✔
911
            booleanProperty(props, PROP_PEDANTIC, b -> this.pedantic = b);
1✔
912

913
            intProperty(props, PROP_MAX_RECONNECT, DEFAULT_MAX_RECONNECT, i -> this.maxReconnect = i);
1✔
914
            durationProperty(props, PROP_RECONNECT_WAIT, DEFAULT_RECONNECT_WAIT, d -> this.reconnectWait = d);
1✔
915
            durationProperty(props, PROP_RECONNECT_JITTER, DEFAULT_RECONNECT_JITTER, d -> this.reconnectJitter = d);
1✔
916
            durationProperty(props, PROP_RECONNECT_JITTER_TLS, DEFAULT_RECONNECT_JITTER_TLS, d -> this.reconnectJitterTls = d);
1✔
917
            longProperty(props, PROP_RECONNECT_BUF_SIZE, DEFAULT_RECONNECT_BUF_SIZE, l -> this.reconnectBufferSize = l);
1✔
918
            durationProperty(props, PROP_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, d -> this.connectionTimeout = d);
1✔
919
            intProperty(props, PROP_SOCKET_READ_TIMEOUT_MS, -1, i -> this.socketReadTimeoutMillis = i);
1✔
920
            durationProperty(props, PROP_SOCKET_WRITE_TIMEOUT, DEFAULT_SOCKET_WRITE_TIMEOUT, d -> this.socketWriteTimeout = d);
1✔
921
            intProperty(props, PROP_SOCKET_SO_LINGER, -1, i -> socketSoLinger = i);
1✔
922

923
            intGtEqZeroProperty(props, PROP_MAX_CONTROL_LINE, DEFAULT_MAX_CONTROL_LINE, i -> this.maxControlLine = i);
1✔
924
            durationProperty(props, PROP_PING_INTERVAL, DEFAULT_PING_INTERVAL, d -> this.pingInterval = d);
1✔
925
            durationProperty(props, PROP_CLEANUP_INTERVAL, DEFAULT_REQUEST_CLEANUP_INTERVAL, d -> this.requestCleanupInterval = d);
1✔
926
            intProperty(props, PROP_MAX_PINGS, DEFAULT_MAX_PINGS_OUT, i -> this.maxPingsOut = i);
1✔
927
            booleanProperty(props, PROP_USE_OLD_REQUEST_STYLE, b -> this.useOldRequestStyle = b);
1✔
928

929
            classnameProperty(props, PROP_ERROR_LISTENER, o -> this.errorListener = (ErrorListener) o);
1✔
930
            classnameProperty(props, PROP_TIME_TRACE_LOGGER, o -> this.timeTraceLogger = (TimeTraceLogger) o);
1✔
931
            classnameProperty(props, PROP_CONNECTION_CB, o -> this.connectionListener = (ConnectionListener) o);
1✔
932
            classnameProperty(props, PROP_READ_LISTENER_CLASS, o -> this.readListener = (ReadListener) o);
1✔
933
            classnameProperty(props, PROP_STATISTICS_COLLECTOR, o -> this.statisticsCollector = (StatisticsCollector) o);
1✔
934

935
            stringProperty(props, PROP_DATA_PORT_TYPE, s -> this.dataPortType = s);
1✔
936
            stringProperty(props, PROP_INBOX_PREFIX, this::inboxPrefix);
1✔
937
            intGtEqZeroProperty(props, PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE, DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE, i -> this.maxMessagesInOutgoingQueue = i);
1✔
938
            booleanProperty(props, PROP_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL, b -> this.discardMessagesWhenOutgoingQueueFull = b);
1✔
939

940
            booleanProperty(props, PROP_IGNORE_DISCOVERED_SERVERS, b -> this.ignoreDiscoveredServers = b);
1✔
941
            booleanProperty(props, PROP_TLS_FIRST, b -> this.tlsFirst = b);
1✔
942
            booleanProperty(props, PROP_USE_TIMEOUT_EXCEPTION, b -> this.useTimeoutException = b);
1✔
943
            booleanProperty(props, PROP_USE_DISPATCHER_WITH_EXECUTOR, b -> this.useDispatcherWithExecutor = b);
1✔
944
            booleanProperty(props, PROP_FORCE_FLUSH_ON_REQUEST, b -> this.forceFlushOnRequest = b);
1✔
945

946
            classnameProperty(props, PROP_SERVERS_POOL_IMPLEMENTATION_CLASS, o -> this.serverPool = (ServerPool) o);
1✔
947
            classnameProperty(props, PROP_DISPATCHER_FACTORY_CLASS, o -> this.dispatcherFactory = (DispatcherFactory) o);
1✔
948
            classnameProperty(props, PROP_EXECUTOR_SERVICE_CLASS, o -> this.executor = (ExecutorService) o);
1✔
949
            classnameProperty(props, PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS, o -> this.scheduledExecutor = (ScheduledExecutorService) o);
1✔
950
            classnameProperty(props, PROP_CONNECT_THREAD_FACTORY_CLASS, o -> this.connectThreadFactory = (ThreadFactory) o);
1✔
951
            classnameProperty(props, PROP_CALLBACK_THREAD_FACTORY_CLASS, o -> this.callbackThreadFactory = (ThreadFactory) o);
1✔
952
            return this;
1✔
953
        }
954

955
        /**
956
         * Add a server to the list of known servers.
957
         *
958
         * @param serverURL the URL for the server to add
959
         * @throws IllegalArgumentException if the url is not formatted correctly.
960
         * @return the Builder for chaining
961
         */
962
        public Builder server(String serverURL) {
963
            return servers(serverURL.trim().split(","));
1✔
964
        }
965

966
        /**
967
         * Add an array of servers to the list of known servers.
968
         *
969
         * @param servers A list of server URIs
970
         * @throws IllegalArgumentException if any url is not formatted correctly.
971
         * @return the Builder for chaining
972
         */
973
        public Builder servers(String[] servers) {
974
            for (String s : servers) {
1✔
975
                if (s != null && !s.isEmpty()) {
1✔
976
                    try {
977
                        String unprocessed = s.trim();
1✔
978
                        NatsUri nuri = new NatsUri(unprocessed);
1✔
979
                        if (!natsServerUris.contains(nuri)) {
1✔
980
                            natsServerUris.add(nuri);
1✔
981
                            unprocessedServers.add(unprocessed);
1✔
982
                        }
983
                    }
984
                    catch (URISyntaxException e) {
1✔
985
                        throw new IllegalArgumentException(e);
1✔
986
                    }
1✔
987
                }
988
            }
989
            return this;
1✔
990
        }
991

992
        /**
993
         * Turn on the old request style that uses a new inbox and subscriber for each
994
         * request.
995
         * @return the Builder for chaining
996
         */
997
        public Builder oldRequestStyle() {
998
            this.useOldRequestStyle = true;
1✔
999
            return this;
1✔
1000
        }
1001

1002
        /**
1003
         * For the default server list provider, turn off server pool randomization.
1004
         * The default provider will pick servers from its list randomly on a reconnect.
1005
         * When noRandomize is set to true the default provider supplies a list that
1006
         * first contains servers as configured and then contains the servers as sent
1007
         * from the connected server.
1008
         * @return the Builder for chaining
1009
         */
1010
        public Builder noRandomize() {
1011
            this.noRandomize = true;
1✔
1012
            return this;
1✔
1013
        }
1014

1015
        /**
1016
         * For the default server list provider, whether to resolve hostnames when building server list.
1017
         * @return the Builder for chaining
1018
         */
1019
        public Builder noResolveHostnames() {
1020
            this.noResolveHostnames = true;
1✔
1021
            return this;
1✔
1022
        }
1023

1024
        public Builder reportNoResponders() {
1025
            this.reportNoResponders = true;
1✔
1026
            return this;
1✔
1027
        }
1028

1029
        /**
1030
         * Turn off echo. If supported by the nats-server version you are connecting to this
1031
         * flag will prevent the server from echoing messages back to the connection if it
1032
         * has subscriptions on the subject being published to.
1033
         * @return the Builder for chaining
1034
         */
1035
        public Builder noEcho() {
1036
            this.noEcho = true;
1✔
1037
            return this;
1✔
1038
        }
1039

1040
        /**
1041
         * Turn off header support. Some versions of the server don't support it.
1042
         * It's also not required if you don't use headers
1043
         * @return the Builder for chaining
1044
         */
1045
        public Builder noHeaders() {
1046
            this.noHeaders = true;
1✔
1047
            return this;
1✔
1048
        }
1049

1050
        /**
1051
         * Turn off noresponder support. Some versions of the server don't support it.
1052
         * @return the Builder for chaining
1053
         */
1054
        public Builder noNoResponders() {
1055
            this.noNoResponders = true;
1✔
1056
            return this;
1✔
1057
        }
1058

1059
        /**
1060
         * Set client side limit checks. Default is true
1061
         * @param checks the checks flag
1062
         * @return the Builder for chaining
1063
         */
1064
        public Builder clientSideLimitChecks(boolean checks) {
1065
            this.clientSideLimitChecks = checks;
1✔
1066
            return this;
1✔
1067
        }
1068

1069
        /**
1070
         * The client protocol is not clear about the encoding for subject names. For
1071
         * performance reasons, the Java client defaults to ASCII. You can enable UTF8
1072
         * with this method. The server, written in go, treats byte to string as UTF8 by default
1073
         * and should allow UTF8 subjects, but make sure to test any clients when using them.
1074
         * @return the Builder for chaining
1075
         */
1076
        public Builder supportUTF8Subjects() {
1077
            this.supportUTF8Subjects = true;
1✔
1078
            return this;
1✔
1079
        }
1080

1081
        /**
1082
         * Set the connection's optional Name.
1083
         *
1084
         * @param name the connections new name.
1085
         * @return the Builder for chaining
1086
         */
1087
        public Builder connectionName(String name) {
1088
            this.connectionName = name;
1✔
1089
            return this;
1✔
1090
        }
1091

1092
        /**
1093
         * Set the connection's inbox prefix. All inboxes will start with this string.
1094
         *
1095
         * @param prefix prefix to use.
1096
         * @return the Builder for chaining
1097
         */
1098
        public Builder inboxPrefix(String prefix) {
1099
            this.inboxPrefix = prefix;
1✔
1100

1101
            if (!this.inboxPrefix.endsWith(".")) {
1✔
1102
                this.inboxPrefix = this.inboxPrefix + ".";
1✔
1103
            }
1104
            return this;
1✔
1105
        }
1106

1107
        /**
1108
         * Turn on verbose mode with the server.
1109
         * @return the Builder for chaining
1110
         */
1111
        public Builder verbose() {
1112
            this.verbose = true;
1✔
1113
            return this;
1✔
1114
        }
1115

1116
        /**
1117
         * Turn on pedantic mode for the server, in relation to this connection.
1118
         * @return the Builder for chaining
1119
         */
1120
        public Builder pedantic() {
1121
            this.pedantic = true;
1✔
1122
            return this;
1✔
1123
        }
1124

1125
        /**
1126
         * Turn on advanced stats, primarily for test/benchmarks. These are visible if you
1127
         * call toString on the {@link Statistics Statistics} object.
1128
         * @return the Builder for chaining
1129
         */
1130
        public Builder turnOnAdvancedStats() {
1131
            this.trackAdvancedStats = true;
1✔
1132
            return this;
1✔
1133
        }
1134

1135
        /**
1136
         * Enable connection trace messages. Messages are printed to standard out. This option is for very
1137
         * fine-grained debugging of connection issues.
1138
         * @return the Builder for chaining
1139
         */
1140
        public Builder traceConnection() {
1141
            this.traceConnection = true;
1✔
1142
            return this;
1✔
1143
        }
1144

1145
        /**
1146
         * Sets the options to use the default SSL Context, if it exists.
1147
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1148
         * @return the Builder for chaining
1149
         */
1150
        public Builder secure() throws NoSuchAlgorithmException {
1151
            useDefaultTls = true;
1✔
1152
            return this;
1✔
1153
        }
1154

1155
        /**
1156
         * Set the options to use an SSL context that accepts any server certificate and has no client certificates.
1157
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1158
         * @return the Builder for chaining
1159
         */
1160
        public Builder opentls() throws NoSuchAlgorithmException {
1161
            useTrustAllTls = true;
1✔
1162
            return this;
1✔
1163
        }
1164

1165
        /**
1166
         * Set the SSL context, requires that the server supports TLS connections and
1167
         * the URI specifies TLS.
1168
         * If provided, the context takes precedence over any other TLS/SSL properties
1169
         * set in the builder, including the sslContextFactory
1170
         * @param ctx the SSL Context to use for TLS connections
1171
         * @return the Builder for chaining
1172
         */
1173
        public Builder sslContext(SSLContext ctx) {
1174
            this.sslContext = ctx;
1✔
1175
            return this;
1✔
1176
        }
1177

1178
        /**
1179
         * Set the factory that provides the ssl context. The factory is superseded
1180
         * by an instance of SSLContext
1181
         * @param sslContextFactory the SSL Context for use to create a ssl context
1182
         * @return the Builder for chaining
1183
         */
1184
        public Builder sslContextFactory(SSLContextFactory sslContextFactory) {
1185
            this.sslContextFactory = sslContextFactory;
1✔
1186
            return this;
1✔
1187
        }
1188

1189
        /**
1190
         *
1191
         * @param keystore the path to the keystore file
1192
         * @return the Builder for chaining
1193
         */
1194
        public Builder keystorePath(String keystore) {
1195
            this.keystore = emptyAsNull(keystore);
1✔
1196
            return this;
1✔
1197
        }
1198

1199
        /**
1200
         *
1201
         * @param keystorePassword the password for the keystore
1202
         * @return the Builder for chaining
1203
         */
1204
        public Builder keystorePassword(char[] keystorePassword) {
1205
            this.keystorePassword = keystorePassword == null || keystorePassword.length == 0 ? null : keystorePassword;
1✔
1206
            return this;
1✔
1207
        }
1208

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

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

1229
        /**
1230
         *
1231
         * @param tlsAlgorithm the tls algorithm. Default is {@value SSLUtils#DEFAULT_TLS_ALGORITHM}
1232
         * @return the Builder for chaining
1233
         */
1234
        public Builder tlsAlgorithm(String tlsAlgorithm) {
1235
            this.tlsAlgorithm = emptyOrNullAs(tlsAlgorithm, DEFAULT_TLS_ALGORITHM);
1✔
1236
            return this;
1✔
1237
        }
1238

1239
        /**
1240
         *
1241
         * @param credentialPath the path to the credentials file for creating an {@link AuthHandler AuthHandler}
1242
         * @return the Builder for chaining
1243
         */
1244
        public Builder credentialPath(String credentialPath) {
1245
            this.credentialPath = emptyAsNull(credentialPath);
1✔
1246
            return this;
1✔
1247
        }
1248

1249
        /**
1250
         * Equivalent to calling maxReconnects with 0, {@link #maxReconnects(int) maxReconnects}.
1251
         * @return the Builder for chaining
1252
         */
1253
        public Builder noReconnect() {
1254
            this.maxReconnect = 0;
1✔
1255
            return this;
1✔
1256
        }
1257

1258
        /**
1259
         * Set the maximum number of reconnect attempts. Use 0 to turn off
1260
         * auto-reconnect. Use -1 to turn on infinite reconnects.
1261
         *
1262
         * <p>The reconnect count is incremented on a per-server basis, so if the server list contains 5 servers
1263
         * but max reconnects is set to 3, only 3 of those servers will be tried.</p>
1264
         *
1265
         * <p>This library has a slight difference from some NATS clients, if you set the maxReconnects to zero
1266
         * there will not be any reconnect attempts, regardless of the number of known servers.</p>
1267
         *
1268
         * <p>The reconnect state is entered when the connection is connected and loses
1269
         * that connection. During the initial connection attempt, the client will cycle over
1270
         * its server list one time, regardless of what maxReconnects is set to. The only exception
1271
         * to this is the async connect method {@link Nats#connectAsynchronously(Options, boolean) connectAsynchronously}.</p>
1272
         *
1273
         * @param max the maximum reconnect attempts
1274
         * @return the Builder for chaining
1275
         */
1276
        public Builder maxReconnects(int max) {
1277
            this.maxReconnect = max;
1✔
1278
            return this;
1✔
1279
        }
1280

1281
        /**
1282
         * Set the time to wait between reconnect attempts to the same server. This setting is only used
1283
         * by the client when the same server appears twice in the reconnect attempts, either because it is the
1284
         * only known server or by random chance. Note, the randomization of the server list doesn't occur per
1285
         * attempt, it is performed once at the start, so if there are 2 servers in the list you will never encounter
1286
         * the reconnect wait.
1287
         *
1288
         * @param time the time to wait
1289
         * @return the Builder for chaining
1290
         */
1291
        public Builder reconnectWait(Duration time) {
1292
            this.reconnectWait = time;
1✔
1293
            return this;
1✔
1294
        }
1295

1296
        /**
1297
         * Set the jitter time to wait between reconnect attempts to the same server. This setting is used to vary
1298
         * the reconnect wait to avoid multiple clients trying to reconnect to servers at the same time.
1299
         *
1300
         * @param time the time to wait
1301
         * @return the Builder for chaining
1302
         */
1303
        public Builder reconnectJitter(Duration time) {
1304
            this.reconnectJitter = time;
1✔
1305
            return this;
1✔
1306
        }
1307

1308
        /**
1309
         * Set the jitter time for a tls/secure connection to wait between reconnect attempts to the same server.
1310
         * This setting is used to vary the reconnect wait to avoid multiple clients trying to reconnect to
1311
         * servers at the same time.
1312
         *
1313
         * @param time the time to wait
1314
         * @return the Builder for chaining
1315
         */
1316
        public Builder reconnectJitterTls(Duration time) {
1317
            this.reconnectJitterTls = time;
1✔
1318
            return this;
1✔
1319
        }
1320

1321
        /**
1322
         * Set the maximum length of a control line sent by this connection. This value is also configured
1323
         * in the server but the protocol doesn't currently forward that setting. Configure it here so that
1324
         * the client can ensure that messages are valid before sending to the server.
1325
         *
1326
         * @param bytes the max byte count
1327
         * @return the Builder for chaining
1328
         */
1329
        public Builder maxControlLine(int bytes) {
1330
            this.maxControlLine = bytes < 0 ? DEFAULT_MAX_CONTROL_LINE : bytes;
1✔
1331
            return this;
1✔
1332
        }
1333

1334
        /**
1335
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1336
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1337
         *
1338
         * @param connectionTimeout the time to wait
1339
         * @return the Builder for chaining
1340
         */
1341
        public Builder connectionTimeout(Duration connectionTimeout) {
1342
            this.connectionTimeout = connectionTimeout;
1✔
1343
            return this;
1✔
1344
        }
1345

1346
        /**
1347
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1348
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1349
         *
1350
         * @param connectionTimeoutMillis the time to wait in milliseconds
1351
         * @return the Builder for chaining
1352
         */
1353
        public Builder connectionTimeout(long connectionTimeoutMillis) {
1354
            this.connectionTimeout = Duration.ofMillis(connectionTimeoutMillis);
×
1355
            return this;
×
1356
        }
1357

1358
        /**
1359
         * Set the timeout to use around socket reads
1360
         * @param socketReadTimeoutMillis the timeout milliseconds
1361
         * @return the Builder for chaining
1362
         */
1363
        public Builder socketReadTimeoutMillis(int socketReadTimeoutMillis) {
1364
            this.socketReadTimeoutMillis = socketReadTimeoutMillis;
1✔
1365
            return this;
1✔
1366
        }
1367

1368
        /**
1369
         * Set the timeout to use around socket writes
1370
         * @param socketWriteTimeoutMillis the timeout milliseconds
1371
         * @return the Builder for chaining
1372
         */
1373
        public Builder socketWriteTimeout(long socketWriteTimeoutMillis) {
1374
            socketWriteTimeout = Duration.ofMillis(socketWriteTimeoutMillis);
1✔
1375
            return this;
1✔
1376
        }
1377

1378
        /**
1379
         * Set the timeout to use around socket writes
1380
         * @param socketWriteTimeout the timeout duration
1381
         * @return the Builder for chaining
1382
         */
1383
        public Builder socketWriteTimeout(Duration socketWriteTimeout) {
1384
            this.socketWriteTimeout = socketWriteTimeout;
1✔
1385
            return this;
1✔
1386
        }
1387

1388
        /**
1389
         * Set the value of the socket SO LINGER property in seconds.
1390
         * This feature is used by library data port implementations.
1391
         * Setting this is a last resort if socket closes are a problem
1392
         * in your environment, otherwise it's generally not necessary
1393
         * to set this. The value must be greater than or equal to 0
1394
         * to have the code call socket.setSoLinger with true and the timeout value
1395
         * @param socketSoLinger the number of seconds to linger
1396
         * @return the Builder for chaining
1397
         */
1398
        public Builder socketSoLinger(int socketSoLinger) {
1399
            this.socketSoLinger = socketSoLinger;
×
1400
            return this;
×
1401
        }
1402

1403
        /**
1404
         * Set the interval between attempts to pings the server. These pings are automated,
1405
         * and capped by {@link #maxPingsOut(int) maxPingsOut()}. As of 2.4.4 the library
1406
         * may wait up to 2 * time to send a ping. Incoming traffic from the server can postpone
1407
         * the next ping to avoid pings taking up bandwidth during busy messaging.
1408
         * Keep in mind that a ping requires a round trip to the server. Setting this value to a small
1409
         * number can result in quick failures due to maxPingsOut being reached, these failures will
1410
         * force a disconnect/reconnect which can result in messages being held back or failed. In general,
1411
         * the ping interval should be set in seconds but this value is not enforced as it would result in
1412
         * an API change from the 2.0 release.
1413
         *
1414
         * @param time the time between client to server pings
1415
         * @return the Builder for chaining
1416
         */
1417
        public Builder pingInterval(Duration time) {
1418
            this.pingInterval = time == null ? DEFAULT_PING_INTERVAL : time;
1✔
1419
            return this;
1✔
1420
        }
1421

1422
        /**
1423
         * Set the interval between cleaning passes on outstanding request futures that are cancelled or timeout
1424
         * in the application code.
1425
         *
1426
         * <p>The default value is probably reasonable, but this interval is useful in a very noisy network
1427
         * situation where lots of requests are used.
1428
         *
1429
         * @param time the cleaning interval
1430
         * @return the Builder for chaining
1431
         */
1432
        public Builder requestCleanupInterval(Duration time) {
1433
            this.requestCleanupInterval = time;
1✔
1434
            return this;
1✔
1435
        }
1436

1437
        /**
1438
         * Set the maximum number of pings the client can have in flight.
1439
         *
1440
         * @param max the max pings
1441
         * @return the Builder for chaining
1442
         */
1443
        public Builder maxPingsOut(int max) {
1444
            this.maxPingsOut = max;
1✔
1445
            return this;
1✔
1446
        }
1447

1448
        /**
1449
         * Sets the initial size for buffers in the connection, primarily for testing.
1450
         * @param size the size in bytes to make buffers for connections created with this options
1451
         * @return the Builder for chaining
1452
         */
1453
        public Builder bufferSize(int size) {
1454
            this.bufferSize = size;
1✔
1455
            return this;
1✔
1456
        }
1457

1458
        /**
1459
         * Set the maximum number of bytes to buffer in the client when trying to
1460
         * reconnect. When this value is exceeded the client will start to drop messages.
1461
         * The count of dropped messages can be read from the {@link Statistics#getDroppedCount() Statistics}.
1462
         * A value of zero will disable the reconnect buffer, a value less than zero means unlimited. Caution
1463
         * should be used for negative numbers as they can result in an unreliable network connection plus a
1464
         * high message rate leading to an out of memory error.
1465
         *
1466
         * @param size the size in bytes
1467
         * @return the Builder for chaining
1468
         */
1469
        public Builder reconnectBufferSize(long size) {
1470
            this.reconnectBufferSize = size;
1✔
1471
            return this;
1✔
1472
        }
1473

1474
        /**
1475
         * Set the username and password for basic authentication.
1476
         * If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
1477
         * these values can be used as a fallback.
1478
         * use the char[] version instead for better security
1479
         *
1480
         * @param userName a non-empty userName
1481
         * @param password the password, in plain text
1482
         * @return the Builder for chaining
1483
         */
1484
        public Builder userInfo(String userName, String password) {
1485
            this.username = userName.toCharArray();
1✔
1486
            this.password = password.toCharArray();
1✔
1487
            return this;
1✔
1488
        }
1489

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

1505
        /**
1506
         * Set the token for token-based authentication.
1507
         * If a token is provided in a server URI, it overrides this value.
1508
         *
1509
         * @param token The token
1510
         * @return the Builder for chaining
1511
         * @deprecated use the char[] version instead for better security
1512
         */
1513
        @Deprecated
1514
        public Builder token(String token) {
1515
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1516
            return this;
1✔
1517
        }
1518

1519
        /**
1520
         * Set the token for token-based authentication.
1521
         * If a token is provided in a server URI, it overrides this value.
1522
         *
1523
         * @param token The token
1524
         * @return the Builder for chaining
1525
         */
1526
        public Builder token(char[] token) {
1527
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1528
            return this;
1✔
1529
        }
1530

1531
        /**
1532
         * Set the token supplier for token-based authentication.
1533
         * If a token is provided in a server URI, it overrides this value.
1534
         *
1535
         * @param tokenSupplier The tokenSupplier
1536
         * @return the Builder for chaining
1537
         */
1538
        public Builder tokenSupplier(Supplier<char[]> tokenSupplier) {
1539
            this.tokenSupplier = tokenSupplier == null ? new DefaultTokenSupplier() : tokenSupplier;
1✔
1540
            return this;
1✔
1541
        }
1542

1543
        /**
1544
         * Set the {@link AuthHandler AuthHandler} to sign the server nonce for authentication in
1545
         * nonce-mode.
1546
         *
1547
         * @param handler The new AuthHandler for this connection.
1548
         * @return the Builder for chaining
1549
         */
1550
        public Builder authHandler(AuthHandler handler) {
1551
            this.authHandler = handler;
1✔
1552
            return this;
1✔
1553
        }
1554

1555
        /**
1556
         * Set the {@link ReconnectDelayHandler ReconnectDelayHandler} for custom reconnect duration
1557
         *
1558
         * @param handler The new ReconnectDelayHandler for this connection.
1559
         * @return the Builder for chaining
1560
         */
1561
        public Builder reconnectDelayHandler(ReconnectDelayHandler handler) {
1562
            this.reconnectDelayHandler = handler;
1✔
1563
            return this;
1✔
1564
        }
1565

1566
        /**
1567
         * Set the {@link ErrorListener ErrorListener} to receive asynchronous error events related to this
1568
         * connection.
1569
         *
1570
         * @param listener The new ErrorListener for this connection.
1571
         * @return the Builder for chaining
1572
         */
1573
        public Builder errorListener(ErrorListener listener) {
1574
            this.errorListener = listener;
1✔
1575
            return this;
1✔
1576
        }
1577

1578
        /**
1579
         * Set the {@link TimeTraceLogger TimeTraceLogger} to receive trace events related to this connection.
1580
         * @param logger The new TimeTraceLogger for this connection.
1581
         * @return the Builder for chaining
1582
         */
1583
        public Builder timeTraceLogger(TimeTraceLogger logger) {
1584
            this.timeTraceLogger = logger;
1✔
1585
            return this;
1✔
1586
        }
1587

1588
        /**
1589
         * Set the {@link ConnectionListener ConnectionListener} to receive asynchronous notifications of disconnect
1590
         * events.
1591
         *
1592
         * @param listener The new ConnectionListener for this type of event.
1593
         * @return the Builder for chaining
1594
         */
1595
        public Builder connectionListener(ConnectionListener listener) {
1596
            this.connectionListener = listener;
1✔
1597
            return this;
1✔
1598
        }
1599

1600
        /**
1601
         * Sets a listener to be notified on incoming protocol/message
1602
         *
1603
         * @param readListener the listener
1604
         * @return the Builder for chaining
1605
         */
1606
        public Builder readListener(ReadListener readListener) {
1607
            this.readListener = readListener;
×
1608
            return this;
×
1609
        }
1610

1611
        /**
1612
         * Set the {@link StatisticsCollector StatisticsCollector} to collect connection metrics.
1613
         * <p>
1614
         * If not set, then a default implementation will be used.
1615
         *
1616
         * @param collector the new StatisticsCollector for this connection.
1617
         * @return the Builder for chaining
1618
         */
1619
        public Builder statisticsCollector(StatisticsCollector collector) {
1620
            this.statisticsCollector = collector;
1✔
1621
            return this;
1✔
1622
        }
1623

1624
        /**
1625
         * Set the {@link ExecutorService ExecutorService} used to run threaded tasks. The default is a
1626
         * cached thread pool that names threads after the connection name (or a default). This executor
1627
         * is used for reading and writing the underlying sockets as well as for each Dispatcher.
1628
         * The default executor uses a short keepalive time, 500ms, to insure quick shutdowns. This is reasonable
1629
         * since most threads from the executor are long-lived. If you customize, be sure to keep the shutdown
1630
         * effect in mind, executors can block for their keepalive time. The default executor also marks threads
1631
         * with priority normal and as non-daemon.
1632
         *
1633
         * @param executor The ExecutorService to use for connections built with these options.
1634
         * @return the Builder for chaining
1635
         */
1636
        public Builder executor(ExecutorService executor) {
1637
            this.executor = executor;
1✔
1638
            return this;
1✔
1639
        }
1640

1641
        /**
1642
         * Set the {@link ScheduledExecutorService ScheduledExecutorService} used to run scheduled task like
1643
         * heartbeat timers
1644
         * The default is a ScheduledThreadPoolExecutor that does not
1645
         *  execute delayed tasks after shutdown and removes tasks on cancel;
1646
         * @param scheduledExecutor The ScheduledExecutorService to use for timer tasks
1647
         * @return the Builder for chaining
1648
         */
1649
        public Builder scheduledExecutor(ScheduledExecutorService scheduledExecutor) {
NEW
1650
            this.scheduledExecutor = scheduledExecutor;
×
NEW
1651
            return this;
×
1652
        }
1653

1654
        /**
1655
         * Sets custom thread factory for the executor service
1656
         *
1657
         * @param threadFactory the thread factory to use for the executor service
1658
         * @return the Builder for chaining
1659
         */
1660
        public Builder connectThreadFactory(ThreadFactory threadFactory) {
1661
            this.connectThreadFactory = threadFactory;
1✔
1662
            return this;
1✔
1663
        }
1664

1665
        /**
1666
         * Sets custom thread factory for the executor service
1667
         *
1668
         * @param threadFactory the thread factory to use for the executor service
1669
         * @return the Builder for chaining
1670
         */
1671
        public Builder callbackThreadFactory(ThreadFactory threadFactory) {
1672
            this.callbackThreadFactory = threadFactory;
1✔
1673
            return this;
1✔
1674
        }
1675

1676
        /**
1677
         * Add an HttpRequest interceptor which can be used to modify the HTTP request when using websockets
1678
         *
1679
         * @param interceptor The interceptor
1680
         * @return the Builder for chaining
1681
         */
1682
        public Builder httpRequestInterceptor(java.util.function.Consumer<HttpRequest> interceptor) {
1683
            if (null == this.httpRequestInterceptors) {
1✔
1684
                this.httpRequestInterceptors = new ArrayList<>();
1✔
1685
            }
1686
            this.httpRequestInterceptors.add(interceptor);
1✔
1687
            return this;
1✔
1688
        }
1689

1690
        /**
1691
         * Overwrite the list of HttpRequest interceptors which can be used to modify the HTTP request when using websockets
1692
         *
1693
         * @param interceptors The list of interceptors
1694
         * @return the Builder for chaining
1695
         */
1696
        public Builder httpRequestInterceptors(Collection<? extends java.util.function.Consumer<HttpRequest>> interceptors) {
1697
            this.httpRequestInterceptors = new ArrayList<>(interceptors);
1✔
1698
            return this;
1✔
1699
        }
1700

1701
        /**
1702
         * Define a proxy to use when connecting.
1703
         *
1704
         * @param proxy is the HTTP or socks proxy to use.
1705
         * @return the Builder for chaining
1706
         */
1707
        public Builder proxy(Proxy proxy) {
1708
            this.proxy = proxy;
1✔
1709
            return this;
1✔
1710
        }
1711

1712
        /**
1713
         * The class to use for this connections data port. This is an advanced setting
1714
         * and primarily useful for testing.
1715
         *
1716
         * @param dataPortClassName a valid and accessible class name
1717
         * @return the Builder for chaining
1718
         */
1719
        public Builder dataPortType(String dataPortClassName) {
1720
            this.dataPortType = dataPortClassName == null ? DEFAULT_DATA_PORT_TYPE : dataPortClassName;
1✔
1721
            return this;
1✔
1722
        }
1723

1724
        /**
1725
         * Set the maximum number of messages in the outgoing queue.
1726
         *
1727
         * @param maxMessagesInOutgoingQueue the maximum number of messages in the outgoing queue
1728
         * @return the Builder for chaining
1729
         */
1730
        public Builder maxMessagesInOutgoingQueue(int maxMessagesInOutgoingQueue) {
1731
            this.maxMessagesInOutgoingQueue = maxMessagesInOutgoingQueue < 0
1✔
1732
                ? DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE
1733
                : maxMessagesInOutgoingQueue;
1734
            return this;
1✔
1735
        }
1736

1737
        /**
1738
         * Enable discard messages when the outgoing queue full. See {@link Builder#maxMessagesInOutgoingQueue(int) maxMessagesInOutgoingQueue}
1739
         *
1740
         * @return the Builder for chaining
1741
         */
1742
        public Builder discardMessagesWhenOutgoingQueueFull() {
1743
            this.discardMessagesWhenOutgoingQueueFull = true;
1✔
1744
            return this;
1✔
1745
        }
1746

1747
        /**
1748
         * Turn off use of discovered servers when connecting / reconnecting. Used in the default server list provider.
1749
         * @return the Builder for chaining
1750
         */
1751
        public Builder ignoreDiscoveredServers() {
1752
            this.ignoreDiscoveredServers = true;
1✔
1753
            return this;
1✔
1754
        }
1755

1756
        /**
1757
         * Set TLS Handshake First behavior on. Default is off.
1758
         * TLS Handshake First is used to instruct the library perform
1759
         * the TLS handshake right after the connect and before receiving
1760
         * the INFO protocol from the server. If this option is enabled
1761
         * but the server is not configured to perform the TLS handshake
1762
         * first, the connection will fail.
1763
         * @return the Builder for chaining
1764
         */
1765
        public Builder tlsFirst() {
1766
            this.tlsFirst = true;
1✔
1767
            return this;
1✔
1768
        }
1769

1770
        /**
1771
         * Throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}?
1772
         * @return the Builder for chaining
1773
         */
1774
        public Builder useTimeoutException() {
1775
            this.useTimeoutException = true;
×
1776
            return this;
×
1777
        }
1778

1779
        /**
1780
         * Instruct dispatchers to dispatch all messages as a task, instead of directly from dispatcher thread
1781
         * @return the Builder for chaining
1782
         */
1783
        public Builder useDispatcherWithExecutor() {
1784
            this.useDispatcherWithExecutor = true;
1✔
1785
            return this;
1✔
1786
        }
1787

1788
        /**
1789
         * Instruct requests to turn off flush on requests.
1790
         * @return the Builder for chaining
1791
         */
1792
        public Builder dontForceFlushOnRequest() {
1793
            this.forceFlushOnRequest = false;
×
1794
            return this;
×
1795
        }
1796

1797
        /**
1798
         * Set the ServerPool implementation for connections to use instead of the default implementation
1799
         * @param serverPool the implementation
1800
         * @return the Builder for chaining
1801
         */
1802
        public Builder serverPool(ServerPool serverPool) {
1803
            this.serverPool = serverPool;
1✔
1804
            return this;
1✔
1805
        }
1806

1807
        /**
1808
         * Set the DispatcherFactory implementation for connections to use instead of the default implementation
1809
         * @param dispatcherFactory the implementation
1810
         * @return the Builder for chaining
1811
         */
1812
        public Builder dispatcherFactory(DispatcherFactory dispatcherFactory) {
1813
            this.dispatcherFactory = dispatcherFactory;
×
1814
            return this;
×
1815
        }
1816

1817
        /**
1818
         * Build an Options object from this Builder.
1819
         *
1820
         * <p>If the Options builder was not provided with a server, a default one will be included
1821
         * {@link Options#DEFAULT_URL}. If only a single server URI is included, the builder
1822
         * will try a few things to make connecting easier:
1823
         * <ul>
1824
         * <li>If there is no user/password is set but the URI has them, {@code nats://user:password@server:port}, they will be used.
1825
         * <li>If there is no token is set but the URI has one, {@code nats://token@server:port}, it will be used.
1826
         * <li>If the URI is of the form tls:// and no SSL context was assigned, one is created, see {@link Options.Builder#secure() secure()}.
1827
         * <li>If the URI is of the form opentls:// and no SSL context was assigned one will be created
1828
         * that does not check the servers certificate for validity. This is not secure and only provided
1829
         * for tests and development.
1830
         * </ul>
1831
         *
1832
         * @return the new options object
1833
         * @throws IllegalStateException if there is a conflict in the options, like a token and a user/pass
1834
         */
1835
        public Options build() throws IllegalStateException {
1836
            // ----------------------------------------------------------------------------------------------------
1837
            // BUILD IMPL
1838
            // ----------------------------------------------------------------------------------------------------
1839
            if (this.username != null && tokenSupplier.get() != null) {
1✔
1840
                throw new IllegalStateException("Options can't have token and username");
1✔
1841
            }
1842

1843
            if (inboxPrefix == null) {
1✔
1844
                inboxPrefix = DEFAULT_INBOX_PREFIX;
×
1845
            }
1846

1847
            boolean checkUrisForSecure = true;
1✔
1848
            if (natsServerUris.isEmpty()) {
1✔
1849
                server(DEFAULT_URL);
1✔
1850
                checkUrisForSecure = false;
1✔
1851
            }
1852

1853
            // ssl context can be directly provided, but if it's not
1854
            // there might be a factory, or just see if we should make it ourselves
1855
            if (sslContext == null) {
1✔
1856
                if (sslContextFactory != null) {
1✔
1857
                    sslContext = sslContextFactory.createSSLContext(new SSLContextFactoryProperties.Builder()
1✔
1858
                        .keystore(keystore)
1✔
1859
                        .keystorePassword(keystorePassword)
1✔
1860
                        .truststore(truststore)
1✔
1861
                        .truststorePassword(truststorePassword)
1✔
1862
                        .tlsAlgorithm(tlsAlgorithm)
1✔
1863
                        .build());
1✔
1864
                }
1865
                else {
1866
                    if (keystore != null || truststore != null) {
1✔
1867
                        // the user provided keystore/truststore properties, the want us to make the sslContext that way
1868
                        try {
1869
                            sslContext = SSLUtils.createSSLContext(keystore, keystorePassword, truststore, truststorePassword, tlsAlgorithm);
1✔
1870
                        }
1871
                        catch (Exception e) {
×
1872
                            throw new IllegalStateException("Unable to create SSL context", e);
×
1873
                        }
1✔
1874
                    }
1875
                    else {
1876
                        // the sslContext has not been requested via factory or keystore/truststore properties
1877
                        // If we haven't been told to use the default or the trust all context
1878
                        // and the server isn't the default url, check to see if the server uris
1879
                        // suggest we need the ssl context.
1880
                        if (!useDefaultTls && !useTrustAllTls && checkUrisForSecure) {
1✔
1881
                            for (int i = 0; sslContext == null && i < natsServerUris.size(); i++) {
1✔
1882
                                NatsUri natsUri = natsServerUris.get(i);
1✔
1883
                                switch (natsUri.getScheme()) {
1✔
1884
                                    case TLS_PROTOCOL:
1885
                                    case SECURE_WEBSOCKET_PROTOCOL:
1886
                                        useDefaultTls = true;
1✔
1887
                                        break;
1✔
1888
                                    case OPENTLS_PROTOCOL:
1889
                                        useTrustAllTls = true;
1✔
1890
                                        break;
1891
                                }
1892
                            }
1893
                        }
1894

1895
                        // check trust all (open) first, in case they provided both
1896
                        // PROP_SECURE (secure) and PROP_OPENTLS (opentls)
1897
                        if (useTrustAllTls) {
1✔
1898
                            try {
1899
                                this.sslContext = SSLUtils.createTrustAllTlsContext();
1✔
1900
                            }
1901
                            catch (GeneralSecurityException e) {
×
1902
                                throw new IllegalStateException("Unable to create SSL context", e);
×
1903
                            }
1✔
1904
                        }
1905
                        else if (useDefaultTls) {
1✔
1906
                            try {
1907
                                this.sslContext = SSLContext.getDefault();
1✔
1908
                            }
1909
                            catch (NoSuchAlgorithmException e) {
×
1910
                                throw new IllegalStateException("Unable to create default SSL context", e);
×
1911
                            }
1✔
1912
                        }
1913
                    }
1914
                }
1915
            }
1916

1917
            if (tlsFirst && sslContext == null) {
1✔
1918
                throw new IllegalStateException("SSL context required for tls handshake first");
×
1919
            }
1920

1921
            if (credentialPath != null) {
1✔
1922
                File file = new File(credentialPath).getAbsoluteFile();
1✔
1923
                authHandler = Nats.credentials(file.toString());
1✔
1924
            }
1925

1926
            if (this.executor == null) {
1✔
1927
                String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
1928
                this.executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
1✔
1929
                    500L, TimeUnit.MILLISECONDS,
1930
                    new SynchronousQueue<>(),
1931
                    new DefaultThreadFactory(threadPrefix));
1932
            }
1933

1934
            if (this.scheduledExecutor == null) {
1✔
1935
                String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
1936
                // the core pool size of 3 is chosen considering where we know the scheduler is used.
1937
                // 1. Ping timer, 2. cleanup timer, 3. SocketDataPortWithWriteTimeout
1938
                // Pull message managers also use a scheduler, but we don't even know if this will be consuming
1939
                ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(3, new DefaultThreadFactory(threadPrefix));
1✔
1940
                stpe.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
1✔
1941
                stpe.setRemoveOnCancelPolicy(true);
1✔
1942
                this.scheduledExecutor = stpe;
1✔
1943
            }
1944

1945
            if (socketReadTimeoutMillis > 0) {
1✔
1946
                long srtMin = pingInterval.toMillis() + MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT;
1✔
1947
                if (socketReadTimeoutMillis < srtMin) {
1✔
1948
                    throw new IllegalStateException("Socket Read Timeout must be at least "
1✔
1949
                        + MINIMUM_SOCKET_READ_TIMEOUT_GT_CONNECTION_TIMEOUT
1950
                        + " milliseconds greater than the Ping Interval");
1951
                }
1952
            }
1953

1954
            if (socketWriteTimeout == null || socketWriteTimeout.toMillis() < 1) {
1✔
1955
                socketWriteTimeout = null;
1✔
1956
            }
1957
            else {
1958
                long swtMin = connectionTimeout.toMillis() + MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT;
1✔
1959
                if (socketWriteTimeout.toMillis() < swtMin) {
1✔
1960
                    throw new IllegalStateException("Socket Write Timeout must be at least "
1✔
1961
                        + MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT
1962
                        + " milliseconds greater than the Connection Timeout");
1963
                }
1964
            }
1965

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2130
        this.serverPool = b.serverPool;
1✔
2131
        this.dispatcherFactory = b.dispatcherFactory;
1✔
2132
    }
1✔
2133

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

2144
    /**
2145
     * @return the ScheduledExecutorService, see {@link Builder#scheduledExecutor(ScheduledExecutorService) scheduledExecutor()} in the builder doc
2146
     */
2147
    public ScheduledExecutorService getScheduledExecutor() {
2148
        return scheduledExecutor;
1✔
2149
    }
2150

2151
    /**
2152
     * @return the callback executor, see {@link Builder#callbackThreadFactory(ThreadFactory) callbackThreadFactory()} in the builder doc
2153
     */
2154
    public ExecutorService getCallbackExecutor() {
2155
        return this.callbackThreadFactory == null ?
1✔
2156
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.callbackThreadFactory);
1✔
2157
    }
2158

2159
    /**
2160
     * @return the connect executor, see {@link Builder#connectThreadFactory(ThreadFactory) connectThreadFactory()} in the builder doc
2161
     */
2162
    public ExecutorService getConnectExecutor() {
2163
        return this.connectThreadFactory == null ?
1✔
2164
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.connectThreadFactory);
1✔
2165
    }
2166

2167
    /**
2168
     * @return the list of HttpRequest interceptors.
2169
     */
2170
    public List<java.util.function.Consumer<HttpRequest>> getHttpRequestInterceptors() {
2171
        return null == this.httpRequestInterceptors
1✔
2172
            ? Collections.emptyList()
1✔
2173
            : Collections.unmodifiableList(this.httpRequestInterceptors);
1✔
2174
    }
2175

2176
    /**
2177
     * @return the proxy to used for all sockets.
2178
     */
2179
    public Proxy getProxy() {
2180
        return this.proxy;
1✔
2181
    }
2182

2183
    /**
2184
     * @return the error listener. Will be an instance of ErrorListenerLoggerImpl if not user supplied. See {@link Builder#errorListener(ErrorListener) errorListener()} in the builder doc
2185
     */
2186
    public ErrorListener getErrorListener() {
2187
        return this.errorListener;
1✔
2188
    }
2189

2190
    /**
2191
     * If the user provided a TimeTraceLogger, it's returned here.
2192
     * If the user set traceConnection but did not supply their own, the original time trace logging will occur
2193
     * If the user did not provide a TimeTraceLogger and did not set traceConnection, this will be a no-op implementation.
2194
     * @return the time trace logger
2195
     */
2196
    public TimeTraceLogger getTimeTraceLogger() {
2197
        return this.timeTraceLogger;
1✔
2198
    }
2199

2200
    /**
2201
     * @return the connection listener, or null, see {@link Builder#connectionListener(ConnectionListener) connectionListener()} in the builder doc
2202
     */
2203
    public ConnectionListener getConnectionListener() {
2204
        return this.connectionListener;
1✔
2205
    }
2206

2207
    /**
2208
     * @return the read listener, or null, see {@link Builder#readListener(ReadListener) readListener()} in the builder doc
2209
     */
2210
    public ReadListener getReadListener() {
2211
        return this.readListener;
1✔
2212
    }
2213

2214
    /**
2215
     * @return the statistics collector, or null, see {@link Builder#statisticsCollector(StatisticsCollector) statisticsCollector()} in the builder doc
2216
     */
2217
    public StatisticsCollector getStatisticsCollector() {
2218
        return this.statisticsCollector;
1✔
2219
    }
2220

2221
    /**
2222
     * @return the auth handler, or null, see {@link Builder#authHandler(AuthHandler) authHandler()} in the builder doc
2223
     */
2224
    public AuthHandler getAuthHandler() {
2225
        return this.authHandler;
1✔
2226
    }
2227

2228
    /**
2229
     * @return the reconnection delay handler, or null, see {@link Builder#reconnectDelayHandler(ReconnectDelayHandler) reconnectDelayHandler()} in the builder doc
2230
     */
2231
    public ReconnectDelayHandler getReconnectDelayHandler() {
2232
        return this.reconnectDelayHandler;
1✔
2233
    }
2234

2235
    /**
2236
     * @return the dataport type for connections created by this options object, see {@link Builder#dataPortType(String) dataPortType()} in the builder doc
2237
     */
2238
    public String getDataPortType() {
2239
        return this.dataPortType;
1✔
2240
    }
2241

2242
    /**
2243
     * @return the data port described by these options
2244
     */
2245
    public DataPort buildDataPort() {
2246
        DataPort dp;
2247
        if (dataPortType.equals(DEFAULT_DATA_PORT_TYPE)) {
1✔
2248
            if (socketWriteTimeout == null) {
1✔
2249
                dp = new SocketDataPort();
1✔
2250
            }
2251
            else {
2252
                dp = new SocketDataPortWithWriteTimeout();
1✔
2253
            }
2254
        }
2255
        else {
2256
            dp = (DataPort) Options.createInstanceOf(dataPortType);
1✔
2257
        }
2258
        dp.afterConstruct(this);
1✔
2259
        return dp;
1✔
2260
    }
2261

2262
    /**
2263
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2264
     */
2265
    public List<URI> getServers() {
2266
        List<URI> list = new ArrayList<>();
1✔
2267
        for (NatsUri nuri : natsServerUris) {
1✔
2268
            list.add(nuri.getUri());
1✔
2269
        }
1✔
2270
        return list;
1✔
2271
    }
2272

2273
    /**
2274
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2275
     */
2276
    public List<NatsUri> getNatsServerUris() {
2277
        return natsServerUris;
1✔
2278
    }
2279

2280
    /**
2281
     * @return the servers as given to the options, since the servers are normalized
2282
     */
2283
    public List<String> getUnprocessedServers() {
2284
        return unprocessedServers;
1✔
2285
    }
2286

2287
    /**
2288
     * @return should we turn off randomization for server connection attempts, see {@link Builder#noRandomize() noRandomize()} in the builder doc
2289
     */
2290
    public boolean isNoRandomize() {
2291
        return noRandomize;
1✔
2292
    }
2293

2294
    /**
2295
     * @return should we resolve hostnames for server connection attempts, see {@link Builder#noResolveHostnames() noResolveHostnames()} in the builder doc
2296
     */
2297
    public boolean isNoResolveHostnames() {
2298
        return noResolveHostnames;
1✔
2299
    }
2300

2301
    /**
2302
     * @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
2303
     */
2304
    public boolean isReportNoResponders() {
2305
        return reportNoResponders;
1✔
2306
    }
2307

2308
    /**
2309
     * @return the connectionName, see {@link Builder#connectionName(String) connectionName()} in the builder doc
2310
     */
2311
    public String getConnectionName() {
2312
        return connectionName;
1✔
2313
    }
2314

2315
    /**
2316
     * @return are we in verbose mode, see {@link Builder#verbose() verbose()} in the builder doc
2317
     */
2318
    public boolean isVerbose() {
2319
        return verbose;
1✔
2320
    }
2321

2322
    /**
2323
     * @return is echo-ing disabled, see {@link Builder#noEcho() noEcho()} in the builder doc
2324
     */
2325
    public boolean isNoEcho() {
2326
        return noEcho;
1✔
2327
    }
2328

2329
    /**
2330
     * @return are headers disabled, see {@link Builder#noHeaders() noHeaders()} in the builder doc
2331
     */
2332
    public boolean isNoHeaders() {
2333
        return noHeaders;
1✔
2334
    }
2335

2336
    /**
2337
     * @return is NoResponders ignored disabled, see {@link Builder#noNoResponders() noNoResponders()} in the builder doc
2338
     */
2339
    public boolean isNoNoResponders() {
2340
        return noNoResponders;
1✔
2341
    }
2342

2343
    /**
2344
     * @return clientSideLimitChecks flag
2345
     */
2346
    public boolean clientSideLimitChecks() {
2347
        return clientSideLimitChecks;
1✔
2348
    }
2349

2350
    /**
2351
     * @return whether utf8 subjects are supported, see {@link Builder#supportUTF8Subjects() supportUTF8Subjects()} in the builder doc.
2352
     */
2353
    public boolean supportUTF8Subjects() {
2354
        return supportUTF8Subjects;
1✔
2355
    }
2356

2357
    /**
2358
     * @return are we using pedantic protocol, see {@link Builder#pedantic() pedantic()} in the builder doc
2359
     */
2360
    public boolean isPedantic() {
2361
        return pedantic;
1✔
2362
    }
2363

2364
    /**
2365
     * @return should we track advanced stats, see {@link Builder#turnOnAdvancedStats() turnOnAdvancedStats()} in the builder doc
2366
     */
2367
    public boolean isTrackAdvancedStats() {
2368
        return trackAdvancedStats;
1✔
2369
    }
2370

2371
    /**
2372
     * If isTraceConnection is true, the user provided a TimeTraceLogger or manually called traceConnection in the builder
2373
     * @return should we trace the connection?
2374
     */
2375
    public boolean isTraceConnection() {
2376
        return traceConnection;
1✔
2377
    }
2378

2379
    /**
2380
     * @return the maximum length of a control line, see {@link Builder#maxControlLine(int) maxControlLine()} in the builder doc
2381
     */
2382
    public int getMaxControlLine() {
2383
        return maxControlLine;
1✔
2384
    }
2385

2386
    /**
2387
     *
2388
     * @return true if there is an sslContext for these Options, otherwise false, see {@link Builder#secure() secure()} in the builder doc
2389
     */
2390
    public boolean isTLSRequired() {
2391
        return sslContext != null;
1✔
2392
    }
2393

2394
    /**
2395
     * @return the sslContext, see {@link Builder#secure() secure()} in the builder doc
2396
     */
2397
    public SSLContext getSslContext() {
2398
        return sslContext;
1✔
2399
    }
2400

2401
    /**
2402
     * @return the maxReconnect attempts to make before failing, see {@link Builder#maxReconnects(int) maxReconnects()} in the builder doc
2403
     */
2404
    public int getMaxReconnect() {
2405
        return maxReconnect;
1✔
2406
    }
2407

2408
    /**
2409
     * @return the reconnectWait, used between reconnect attempts, see {@link Builder#reconnectWait(Duration) reconnectWait()} in the builder doc
2410
     */
2411
    public Duration getReconnectWait() {
2412
        return reconnectWait;
1✔
2413
    }
2414

2415
    /**
2416
     * @return the reconnectJitter, used between reconnect attempts to vary the reconnect wait, see {@link Builder#reconnectJitter(Duration) reconnectJitter()} in the builder doc
2417
     */
2418
    public Duration getReconnectJitter() {
2419
        return reconnectJitter;
1✔
2420
    }
2421

2422
    /**
2423
     * @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
2424
     */
2425
    public Duration getReconnectJitterTls() {
2426
        return reconnectJitterTls;
1✔
2427
    }
2428

2429
    /**
2430
     * @return the connectionTimeout, see {@link Builder#connectionTimeout(Duration) connectionTimeout()} in the builder doc
2431
     */
2432
    public Duration getConnectionTimeout() {
2433
        return connectionTimeout;
1✔
2434
    }
2435

2436
    /**
2437
     * @return the socketReadTimeoutMillis, see {@link Builder#socketReadTimeoutMillis(int) socketReadTimeoutMillis} in the builder doc
2438
     */
2439
    public int getSocketReadTimeoutMillis() {
2440
        return socketReadTimeoutMillis;
1✔
2441
    }
2442

2443
    /**
2444
     * @return the socketWriteTimeout, see {@link Builder#socketWriteTimeout(long) socketWriteTimeout} in the builder doc
2445
     */
2446
    public Duration getSocketWriteTimeout() {
2447
        return socketWriteTimeout;
1✔
2448
    }
2449

2450
    /**
2451
     * @return the socket so linger number of seconds, see {@link Builder#socketSoLinger(int) socketSoLinger()} in the builder doc
2452
     */
2453
    public int getSocketSoLinger() {
2454
        return socketSoLinger;
1✔
2455
    }
2456

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

2464
    /**
2465
     * @return the request cleanup interval, see {@link Builder#requestCleanupInterval(Duration) requestCleanupInterval()} in the builder doc
2466
     */
2467
    public Duration getRequestCleanupInterval() {
2468
        return requestCleanupInterval;
1✔
2469
    }
2470

2471
    /**
2472
     * @return the maxPingsOut to limit the number of pings on the wire, see {@link Builder#maxPingsOut(int) maxPingsOut()} in the builder doc
2473
     */
2474
    public int getMaxPingsOut() {
2475
        return maxPingsOut;
1✔
2476
    }
2477

2478
    /**
2479
     * @return the reconnectBufferSize, to limit the amount of data held during
2480
     *         reconnection attempts, see {@link Builder#reconnectBufferSize(long) reconnectBufferSize()} in the builder doc
2481
     */
2482
    public long getReconnectBufferSize() {
2483
        return reconnectBufferSize;
1✔
2484
    }
2485

2486
    /**
2487
     * @return the default size for buffers in the connection code, see {@link Builder#bufferSize(int) bufferSize()} in the builder doc
2488
     */
2489
    public int getBufferSize() {
2490
        return bufferSize;
1✔
2491
    }
2492

2493
    /**
2494
     * @deprecated converts the char array to a string, use getUserNameChars instead for more security
2495
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2496
     */
2497
    @Deprecated
2498
    public String getUsername() {
2499
        return username == null ? null : new String(username);
1✔
2500
    }
2501

2502
    /**
2503
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2504
     */
2505
    public char[] getUsernameChars() {
2506
        return username;
1✔
2507
    }
2508

2509
    /**
2510
     * @deprecated converts the char array to a string, use getPasswordChars instead for more security
2511
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2512
     */
2513
    @Deprecated
2514
    public String getPassword() {
2515
        return password == null ? null : new String(password);
1✔
2516
    }
2517

2518
    /**
2519
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2520
     */
2521
    public char[] getPasswordChars() {
2522
        return password;
1✔
2523
    }
2524

2525
    /**
2526
     * @deprecated converts the char array to a string, use getTokenChars instead for more security
2527
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2528
     */
2529
    @Deprecated
2530
    public String getToken() {
2531
        char[] token = tokenSupplier.get();
1✔
2532
        return token == null ? null : new String(token);
1✔
2533
    }
2534

2535
    /**
2536
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2537
     */
2538
    public char[] getTokenChars() {
2539
        return tokenSupplier.get();
1✔
2540
    }
2541

2542
    /**
2543
     * @return the flag to turn on old style requests, see {@link Builder#oldRequestStyle() oldStyleRequest()} in the builder doc
2544
     */
2545
    public boolean isOldRequestStyle() {
2546
        return useOldRequestStyle;
1✔
2547
    }
2548

2549
    /**
2550
     * @return the inbox prefix to use for requests, see {@link Builder#inboxPrefix(String) inboxPrefix()} in the builder doc
2551
     */
2552
    public String getInboxPrefix() {
2553
        return inboxPrefix;
1✔
2554
    }
2555

2556
    /**
2557
     * @return the maximum number of messages in the outgoing queue, see {@link Builder#maxMessagesInOutgoingQueue(int)
2558
     * maxMessagesInOutgoingQueue(int)} in the builder doc
2559
     */
2560
    public int getMaxMessagesInOutgoingQueue() {
2561
        return maxMessagesInOutgoingQueue;
1✔
2562
    }
2563

2564
    /**
2565
     * @return should we discard messages when the outgoing queue is full, see {@link Builder#discardMessagesWhenOutgoingQueueFull()
2566
     * discardMessagesWhenOutgoingQueueFull()} in the builder doc
2567
     */
2568
    public boolean isDiscardMessagesWhenOutgoingQueueFull() {
2569
        return discardMessagesWhenOutgoingQueueFull;
1✔
2570
    }
2571

2572
    /**
2573
     * Get whether to ignore discovered servers
2574
     * @return the flag
2575
     */
2576
    public boolean isIgnoreDiscoveredServers() {
2577
        return ignoreDiscoveredServers;
1✔
2578
    }
2579

2580
    /**
2581
     * Get whether to do tls first
2582
     * @return the flag
2583
     */
2584
    public boolean isTlsFirst() {
2585
        return tlsFirst;
1✔
2586
    }
2587

2588
    /**
2589
     * Get whether to throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}.
2590
     * @return the flag
2591
     */
2592
    public boolean useTimeoutException() {
2593
        return useTimeoutException;
1✔
2594
    }
2595

2596
    /**
2597
     * Whether the dispatcher should use an executor to async messages to handlers
2598
     * @return the flag
2599
     */
2600
    public boolean useDispatcherWithExecutor() { return useDispatcherWithExecutor; }
1✔
2601

2602
    /**
2603
     * Whether to flush on any user request
2604
     * @return the flag
2605
     */
2606
    public boolean forceFlushOnRequest() {
2607
        return forceFlushOnRequest;
1✔
2608
    }
2609

2610
    /**
2611
     * Get the ServerPool implementation. If null, a default implementation is used.
2612
     * @return the ServerPool implementation
2613
     */
2614
    public ServerPool getServerPool() {
2615
        return serverPool;
1✔
2616
    }
2617

2618
    /**
2619
     * Get the DispatcherFactory implementation. If null, a default implementation is used.
2620
     * @return the DispatcherFactory implementation
2621
     */
2622
    public DispatcherFactory getDispatcherFactory() {
2623
        return dispatcherFactory;
1✔
2624
    }
2625

2626
    public URI createURIForServer(String serverURI) throws URISyntaxException {
2627
        return new NatsUri(serverURI).getUri();
1✔
2628
    }
2629

2630
    /**
2631
     * Create the options string sent with the connect message.
2632
     * If includeAuth is true the auth information is included:
2633
     * If the server URIs have auth info it is used. Otherwise, the userInfo is used.
2634
     * @param serverURI the current server uri
2635
     * @param includeAuth tells the options to build a connection string that includes auth information
2636
     * @param nonce if the client is supposed to sign the nonce for authentication
2637
     * @return the options String, basically JSON
2638
     */
2639
    public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean includeAuth, byte[] nonce) {
2640
        CharBuffer connectString = CharBuffer.allocate(this.maxControlLine);
1✔
2641
        connectString.append("{");
1✔
2642

2643
        appendOption(connectString, Options.OPTION_LANG, Nats.CLIENT_LANGUAGE, true, false);
1✔
2644
        appendOption(connectString, Options.OPTION_VERSION, Nats.CLIENT_VERSION, true, true);
1✔
2645

2646
        if (this.connectionName != null) {
1✔
2647
            appendOption(connectString, Options.OPTION_NAME, this.connectionName, true, true);
1✔
2648
        }
2649

2650
        appendOption(connectString, Options.OPTION_PROTOCOL, "1", false, true);
1✔
2651

2652
        appendOption(connectString, Options.OPTION_VERBOSE, String.valueOf(this.isVerbose()), false, true);
1✔
2653
        appendOption(connectString, Options.OPTION_PEDANTIC, String.valueOf(this.isPedantic()), false, true);
1✔
2654
        appendOption(connectString, Options.OPTION_TLS_REQUIRED, String.valueOf(this.isTLSRequired()), false, true);
1✔
2655
        appendOption(connectString, Options.OPTION_ECHO, String.valueOf(!this.isNoEcho()), false, true);
1✔
2656
        appendOption(connectString, Options.OPTION_HEADERS, String.valueOf(!this.isNoHeaders()), false, true);
1✔
2657
        appendOption(connectString, Options.OPTION_NORESPONDERS, String.valueOf(!this.isNoNoResponders()), false, true);
1✔
2658

2659
        if (includeAuth) {
1✔
2660
            if (nonce != null && this.getAuthHandler() != null) {
1✔
2661
                char[] nkey = this.getAuthHandler().getID();
1✔
2662
                byte[] sig = this.getAuthHandler().sign(nonce);
1✔
2663
                char[] jwt = this.getAuthHandler().getJWT();
1✔
2664

2665
                if (sig == null) {
1✔
2666
                    sig = new byte[0];
1✔
2667
                }
2668

2669
                if (jwt == null) {
1✔
2670
                    jwt = new char[0];
1✔
2671
                }
2672

2673
                if (nkey == null) {
1✔
2674
                    nkey = new char[0];
1✔
2675
                }
2676

2677
                String encodedSig = base64UrlEncodeToString(sig);
1✔
2678

2679
                appendOption(connectString, Options.OPTION_NKEY, nkey, true);
1✔
2680
                appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
1✔
2681
                appendOption(connectString, Options.OPTION_JWT, jwt, true);
1✔
2682
            }
2683

2684
            String uriUser = null;
1✔
2685
            String uriPass = null;
1✔
2686
            String uriToken = null;
1✔
2687

2688
            // Values from URI override options
2689
            try {
2690
                URI uri = this.createURIForServer(serverURI);
1✔
2691
                String userInfo = uri.getRawUserInfo();
1✔
2692
                if (userInfo != null) {
1✔
2693
                    int at = userInfo.indexOf(":");
1✔
2694
                    if (at == -1) {
1✔
2695
                        uriToken = uriDecode(userInfo);
1✔
2696
                    }
2697
                    else {
2698
                        uriUser = uriDecode(userInfo.substring(0, at));
1✔
2699
                        uriPass = uriDecode(userInfo.substring(at + 1));
1✔
2700
                    }
2701
                }
2702
            }
2703
            catch (URISyntaxException e) {
×
2704
                // the createURIForServer call is the one that potentially throws this
2705
                // uriUser, uriPass and uriToken will already be null
2706
            }
1✔
2707

2708
            if (uriUser != null) {
1✔
2709
                appendOption(connectString, Options.OPTION_USER, jsonEncode(uriUser), true, true);
1✔
2710
            }
2711
            else if (this.username != null) {
1✔
2712
                appendOption(connectString, Options.OPTION_USER, jsonEncode(this.username), true, true);
1✔
2713
            }
2714

2715
            if (uriPass != null) {
1✔
2716
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(uriPass), true, true);
1✔
2717
            }
2718
            else if (this.password != null) {
1✔
2719
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(this.password), true, true);
1✔
2720
            }
2721

2722
            if (uriToken != null) {
1✔
2723
                appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
1✔
2724
            }
2725
            else {
2726
                char[] token = this.tokenSupplier.get();
1✔
2727
                if (token != null) {
1✔
2728
                    appendOption(connectString, Options.OPTION_AUTH_TOKEN, token, true);
1✔
2729
                }
2730
            }
2731
        }
2732

2733
        connectString.append("}");
1✔
2734
        connectString.flip();
1✔
2735
        return connectString;
1✔
2736
    }
2737

2738
    // ----------------------------------------------------------------------------------------------------
2739
    // HELPER FUNCTIONS
2740
    // ----------------------------------------------------------------------------------------------------
2741
    private static void appendOption(CharBuffer builder, String key, String value, boolean quotes, boolean comma) {
2742
        _appendStart(builder, key, quotes, comma);
1✔
2743
        builder.append(value);
1✔
2744
        _appendOptionEnd(builder, quotes);
1✔
2745
    }
1✔
2746

2747
    @SuppressWarnings("SameParameterValue")
2748
    private static void appendOption(CharBuffer builder, String key, char[] value, boolean comma) {
2749
        _appendStart(builder, key, true, comma);
1✔
2750
        builder.put(value);
1✔
2751
        _appendOptionEnd(builder, true);
1✔
2752
    }
1✔
2753

2754
    private static void _appendStart(CharBuffer builder, String key, boolean quotes, boolean comma) {
2755
        if (comma) {
1✔
2756
            builder.append(',');
1✔
2757
        }
2758
        builder.append('"');
1✔
2759
        builder.append(key);
1✔
2760
        builder.append('"');
1✔
2761
        builder.append(':');
1✔
2762
        _appendOptionEnd(builder, quotes);
1✔
2763
    }
1✔
2764

2765
    private static void _appendOptionEnd(CharBuffer builder, boolean quotes) {
2766
        if (quotes) {
1✔
2767
            builder.append('"');
1✔
2768
        }
2769
    }
1✔
2770

2771
    private static String getPropertyValue(Properties props, String key) {
2772
        String value = emptyAsNull(props.getProperty(key));
1✔
2773
        if (value != null) {
1✔
2774
            return value;
1✔
2775
        }
2776
        if (key.startsWith(PFX)) { // if the key starts with the PFX, check the non PFX
1✔
2777
            return emptyAsNull(props.getProperty(key.substring(PFX_LEN)));
1✔
2778
        }
2779
        // otherwise check with the PFX
2780
        value = emptyAsNull(props.getProperty(PFX + key));
1✔
2781
        if (value == null && key.contains("_")) {
1✔
2782
            // addressing where underscore was used in a key value instead of dot
2783
            return getPropertyValue(props, key.replace("_", "."));
1✔
2784
        }
2785
        return value;
1✔
2786
    }
2787

2788
    private static void stringProperty(Properties props, String key, java.util.function.Consumer<String> consumer) {
2789
        String value = getPropertyValue(props, key);
1✔
2790
        if (value != null) {
1✔
2791
            consumer.accept(value);
1✔
2792
        }
2793
    }
1✔
2794

2795
    private static void charArrayProperty(Properties props, String key, java.util.function.Consumer<char[]> consumer) {
2796
        String value = getPropertyValue(props, key);
1✔
2797
        if (value != null) {
1✔
2798
            consumer.accept(value.toCharArray());
1✔
2799
        }
2800
    }
1✔
2801

2802
    private static void booleanProperty(Properties props, String key, java.util.function.Consumer<Boolean> consumer) {
2803
        String value = getPropertyValue(props, key);
1✔
2804
        if (value != null) {
1✔
2805
            consumer.accept(Boolean.parseBoolean(value));
1✔
2806
        }
2807
    }
1✔
2808

2809
    private static void intProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2810
        String value = getPropertyValue(props, key);
1✔
2811
        if (value == null) {
1✔
2812
            consumer.accept(defaultValue);
1✔
2813
        }
2814
        else {
2815
            consumer.accept(Integer.parseInt(value));
1✔
2816
        }
2817
    }
1✔
2818

2819
    private static void intGtEqZeroProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2820
        String value = getPropertyValue(props, key);
1✔
2821
        if (value == null) {
1✔
2822
            consumer.accept(defaultValue);
1✔
2823
        }
2824
        else {
2825
            int i = Integer.parseInt(value);
1✔
2826
            if (i < 0) {
1✔
2827
                consumer.accept(defaultValue);
1✔
2828
            }
2829
            else {
2830
                consumer.accept(i);
1✔
2831
            }
2832
        }
2833
    }
1✔
2834

2835
    private static void longProperty(Properties props, String key, long defaultValue, java.util.function.Consumer<Long> consumer) {
2836
        String value = getPropertyValue(props, key);
1✔
2837
        if (value == null) {
1✔
2838
            consumer.accept(defaultValue);
1✔
2839
        }
2840
        else {
2841
            consumer.accept(Long.parseLong(value));
1✔
2842
        }
2843
    }
1✔
2844

2845
    private static void durationProperty(Properties props, String key, Duration defaultValue, java.util.function.Consumer<Duration> consumer) {
2846
        String value = getPropertyValue(props, key);
1✔
2847
        if (value == null) {
1✔
2848
            consumer.accept(defaultValue);
1✔
2849
        }
2850
        else {
2851
            try {
2852
                Duration d = Duration.parse(value);
1✔
2853
                if (d.toNanos() < 0) {
1✔
2854
                    consumer.accept(defaultValue);
×
2855
                }
2856
                else {
2857
                    consumer.accept(d);
1✔
2858
                }
2859
            }
2860
            catch (DateTimeParseException pe) {
1✔
2861
                int ms = Integer.parseInt(value);
1✔
2862
                if (ms < 0) {
1✔
2863
                    consumer.accept(defaultValue);
1✔
2864
                }
2865
                else {
2866
                    consumer.accept(Duration.ofMillis(ms));
1✔
2867
                }
2868
            }
1✔
2869
        }
2870
    }
1✔
2871

2872
    private static void classnameProperty(Properties props, String key, java.util.function.Consumer<Object> consumer) {
2873
        stringProperty(props, key, className -> consumer.accept(createInstanceOf(className)));
1✔
2874
    }
1✔
2875

2876
    private static Object createInstanceOf(String className) {
2877
        try {
2878
            Class<?> clazz = Class.forName(className);
1✔
2879
            Constructor<?> constructor = clazz.getConstructor();
1✔
2880
            return constructor.newInstance();
1✔
2881
        } catch (Exception e) {
1✔
2882
            throw new IllegalArgumentException(e);
1✔
2883
        }
2884
    }
2885
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc