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

nats-io / nats.java / #2218

25 Sep 2025 01:31PM UTC coverage: 95.571% (-0.02%) from 95.586%
#2218

push

github

web-flow
Merge pull request #1432 from nats-io/socket-danger

Options to set underlying socket configuration of SO_SNDBUF and SO_RCVBUF

21 of 28 new or added lines in 2 files covered. (75.0%)

3 existing lines in 2 files now uncovered.

12106 of 12667 relevant lines covered (95.57%)

0.96 hits per line

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

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

14
package io.nats.client;
15

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

552
    /**
553
     * Property used to enable fast fallback algorithm for socket connection.
554
     * {@link Builder#enableFastFallback() enableFastFallback}.
555
     */
556
    public static final String PROP_FAST_FALLBACK = PFX + "fast.fallback";
557

558
    // ----------------------------------------------------------------------------------------------------
559
    // PROTOCOL CONNECT OPTION CONSTANTS
560
    // ----------------------------------------------------------------------------------------------------
561
    /**
562
     * Protocol key {@value}, see {@link Builder#verbose() verbose}.
563
     */
564
    static final String OPTION_VERBOSE = "verbose";
565

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

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

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

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

590
    /**
591
     * Protocol key {@value}, see
592
     * {@link Builder#userInfo(String, String) userInfo}.
593
     */
594
    static final String OPTION_PASSWORD = "pass";
595

596
    /**
597
     * Protocol key {@value}, see {@link Builder#connectionName(String)
598
     * connectionName}.
599
     */
600
    static final String OPTION_NAME = "name";
601

602
    /**
603
     * Protocol key {@value}, will be set to "Java".
604
     */
605
    static final String OPTION_LANG = "lang";
606

607
    /**
608
     * Protocol key {@value}, will be set to
609
     * {@link Nats#CLIENT_VERSION CLIENT_VERSION}.
610
     */
611
    static final String OPTION_VERSION = "version";
612

613
    /**
614
     * Protocol key {@value}, will be set to 1.
615
     */
616
    static final String OPTION_PROTOCOL = "protocol";
617

618
    /**
619
     * Echo key {@value}, determines if the server should echo to the client.
620
     */
621
    static final String OPTION_ECHO = "echo";
622

623
    /**
624
     * NKey key {@value}, the public key being used for sign-in.
625
     */
626
    static final String OPTION_NKEY = "nkey";
627

628
    /**
629
     * SIG key {@value}, the signature of the nonce sent by the server.
630
     */
631
    static final String OPTION_SIG = "sig";
632

633
    /**
634
     * JWT key {@value}, the user JWT to send to the server.
635
     */
636
    static final String OPTION_JWT = "jwt";
637

638
    /**
639
     * Headers key if headers are supported
640
     */
641
    static final String OPTION_HEADERS = "headers";
642

643
    /**
644
     * No Responders key if noresponders are supported
645
     */
646
    static final String OPTION_NORESPONDERS = "no_responders";
647

648
    // ----------------------------------------------------------------------------------------------------
649
    // CLASS VARIABLES
650
    // ----------------------------------------------------------------------------------------------------
651
    private final List<NatsUri> natsServerUris;
652
    private final List<String> unprocessedServers;
653
    private final boolean noRandomize;
654
    private final boolean noResolveHostnames;
655
    private final boolean reportNoResponders;
656
    private final String connectionName;
657
    private final boolean verbose;
658
    private final boolean pedantic;
659
    private final SSLContext sslContext;
660
    private final int maxReconnect;
661
    private final int maxControlLine;
662
    private final Duration reconnectWait;
663
    private final Duration reconnectJitter;
664
    private final Duration reconnectJitterTls;
665
    private final Duration connectionTimeout;
666
    private final int socketReadTimeoutMillis;
667
    private final Duration socketWriteTimeout;
668
    private final int socketSoLinger;
669
    private final int receiveBufferSize;
670
    private final int sendBufferSize;
671
    private final Duration pingInterval;
672
    private final Duration requestCleanupInterval;
673
    private final int maxPingsOut;
674
    private final long reconnectBufferSize;
675
    private final char[] username;
676
    private final char[] password;
677
    private final Supplier<char[]> tokenSupplier;
678
    private final String inboxPrefix;
679
    private boolean useOldRequestStyle;
680
    private final int bufferSize;
681
    private final boolean noEcho;
682
    private final boolean noHeaders;
683
    private final boolean noNoResponders;
684
    private final boolean clientSideLimitChecks;
685
    private final boolean supportUTF8Subjects;
686
    private final int maxMessagesInOutgoingQueue;
687
    private final boolean discardMessagesWhenOutgoingQueueFull;
688
    private final boolean ignoreDiscoveredServers;
689
    private final boolean tlsFirst;
690
    private final boolean useTimeoutException;
691
    private final boolean useDispatcherWithExecutor;
692
    private final boolean forceFlushOnRequest;
693

694
    private final AuthHandler authHandler;
695
    private final ReconnectDelayHandler reconnectDelayHandler;
696

697
    private final ErrorListener errorListener;
698
    private final TimeTraceLogger timeTraceLogger;
699
    private final ConnectionListener connectionListener;
700
    private final ReadListener readListener;
701
    private final StatisticsCollector statisticsCollector;
702
    private final String dataPortType;
703

704
    private final boolean trackAdvancedStats;
705
    private final boolean traceConnection;
706

707
    private final ExecutorService executor;
708
    private final ScheduledExecutorService scheduledExecutor;
709
    private final ThreadFactory connectThreadFactory;
710
    private final ThreadFactory callbackThreadFactory;
711
    private final ServerPool serverPool;
712
    private final DispatcherFactory dispatcherFactory;
713

714
    private final List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
715
    private final Proxy proxy;
716
    private final boolean enableFastFallback;
717

718
    static class DefaultThreadFactory implements ThreadFactory {
719
        final String name;
720
        final AtomicInteger threadNo;
721

722
        public DefaultThreadFactory (String name){
1✔
723
            this.name = name;
1✔
724
            threadNo = new AtomicInteger(0);
1✔
725
        }
1✔
726

727
        public Thread newThread(@NonNull Runnable r) {
728
            String threadName = name+":"+threadNo.incrementAndGet();
1✔
729
            Thread t = new Thread(r,threadName);
1✔
730
            if (t.isDaemon()) {
1✔
731
                t.setDaemon(false);
×
732
            }
733
            if (t.getPriority() != Thread.NORM_PRIORITY) {
1✔
734
                t.setPriority(Thread.NORM_PRIORITY);
×
735
            }
736
            return t;
1✔
737
        }
738
    }
739

740
    static class DefaultTokenSupplier implements Supplier<char[]> {
741
        final char[] token;
742

743
        public DefaultTokenSupplier() {
1✔
744
            token = null;
1✔
745
        }
1✔
746

747
        public DefaultTokenSupplier(char[] token) {
1✔
748
            this.token = token == null || token.length == 0 ? null : token;
1✔
749
        }
1✔
750

751
        public DefaultTokenSupplier(String token) {
1✔
752
            token = Validator.emptyAsNull(token);
1✔
753
            this.token = token == null ? null : token.toCharArray();
1✔
754
        }
1✔
755

756
        @Override
757
        public char[] get() {
758
            return token;
1✔
759
        }
760
    }
761

762
    /**
763
     * Set old request style.
764
     * @param value true to use the old request style
765
     * @deprecated Use Builder
766
     */
767
    @Deprecated
768
    public void setOldRequestStyle(boolean value) {
769
        useOldRequestStyle = value;
1✔
770
    }
1✔
771

772
    // ----------------------------------------------------------------------------------------------------
773
    // BUILDER
774
    // ----------------------------------------------------------------------------------------------------
775
    /**
776
     * Creates a builder for the options in a fluent style
777
     * @return the builder.
778
     */
779
    public static Builder builder() {
780
        return new Builder();
1✔
781
    }
782

783
    /**
784
     * Options are created using a Builder. The builder supports chaining and will
785
     * create a default set of options if no methods are calls. The builder can also
786
     * be created from a properties object using the property names defined with the
787
     * prefix PROP_ in this class.
788
     * <p>A common usage for testing might be {@code new Options.Builder().server(myserverurl).noReconnect.build()}
789
     */
790
    public static class Builder {
791

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

843
        private AuthHandler authHandler;
844
        private ReconnectDelayHandler reconnectDelayHandler;
845

846
        private ErrorListener errorListener = null;
1✔
847
        private TimeTraceLogger timeTraceLogger = null;
1✔
848
        private ConnectionListener connectionListener = null;
1✔
849
        private ReadListener readListener = null;
1✔
850
        private StatisticsCollector statisticsCollector = null;
1✔
851
        private String dataPortType = DEFAULT_DATA_PORT_TYPE;
1✔
852
        private ExecutorService executor;
853
        private ScheduledExecutorService scheduledExecutor;
854
        private ThreadFactory connectThreadFactory;
855
        private ThreadFactory callbackThreadFactory;
856
        private List<java.util.function.Consumer<HttpRequest>> httpRequestInterceptors;
857
        private Proxy proxy;
858

859
        private boolean useDefaultTls;
860
        private boolean useTrustAllTls;
861
        private String keystore;
862
        private char[] keystorePassword;
863
        private String truststore;
864
        private char[] truststorePassword;
865
        private String tlsAlgorithm = DEFAULT_TLS_ALGORITHM;
1✔
866
        private String credentialPath;
867
        private boolean enableFastFallback = false;
1✔
868

869
        /**
870
         * Constructs a new Builder with the default values.
871
         * <p>When {@link #build() build()} is called on a default builder it will add the {@link Options#DEFAULT_URL
872
         * default url} to its list of servers if there were no servers defined.</p>
873
         */
874
        public Builder() {}
1✔
875

876
        // ----------------------------------------------------------------------------------------------------
877
        // BUILD CONSTRUCTOR PROPS
878
        // ----------------------------------------------------------------------------------------------------
879
        /**
880
         * Constructs a new {@code Builder} from a {@link Properties} object.
881
         * <p>Methods called on the builder after construction can override the properties.</p>
882
         * @param props the {@link Properties} object
883
         */
884
        public Builder(Properties props) throws IllegalArgumentException {
1✔
885
            properties(props);
1✔
886
        }
1✔
887

888
        /**
889
         * Constructs a new {@code Builder} from a file that contains properties.
890
         * @param propertiesFilePath a resolvable path to a file from the location the application is running, either relative or absolute
891
         * @throws IOException if the properties file cannot be found, opened or read
892
         */
893
        public Builder(String propertiesFilePath) throws IOException {
1✔
894
            Properties props = new Properties();
1✔
895
            props.load(Files.newInputStream(Paths.get(propertiesFilePath)));
1✔
896
            properties(props);
1✔
897
        }
1✔
898

899
        // ----------------------------------------------------------------------------------------------------
900
        // BUILDER METHODS
901
        // ----------------------------------------------------------------------------------------------------
902

903
        /**
904
         * Add settings defined in the properties object
905
         * @param props the properties object
906
         * @throws IllegalArgumentException if the properties object is null
907
         * @return the Builder for chaining
908
         */
909
        public Builder properties(Properties props) {
910
            if (props == null) {
1✔
911
                throw new IllegalArgumentException("Properties cannot be null");
1✔
912
            }
913
            stringProperty(props, PROP_URL, this::server);
1✔
914
            stringProperty(props, PROP_SERVERS, str -> {
1✔
915
                String[] servers = str.trim().split(",\\s*");
1✔
916
                this.servers(servers);
1✔
917
            });
1✔
918

919
            charArrayProperty(props, PROP_USERNAME, ca -> this.username = ca);
1✔
920
            charArrayProperty(props, PROP_PASSWORD, ca -> this.password = ca);
1✔
921
            charArrayProperty(props, PROP_TOKEN, ca -> this.tokenSupplier = new DefaultTokenSupplier(ca));
1✔
922
            //noinspection unchecked
923
            classnameProperty(props, PROP_TOKEN_SUPPLIER, o -> this.tokenSupplier = (Supplier<char[]>) o);
1✔
924

925
            booleanProperty(props, PROP_SECURE, b -> this.useDefaultTls = b);
1✔
926
            booleanProperty(props, PROP_OPENTLS, b -> this.useTrustAllTls = b);
1✔
927

928
            classnameProperty(props, PROP_SSL_CONTEXT_FACTORY_CLASS, o -> this.sslContextFactory = (SSLContextFactory) o);
1✔
929
            stringProperty(props, PROP_KEYSTORE, s -> this.keystore = s);
1✔
930
            charArrayProperty(props, PROP_KEYSTORE_PASSWORD, ca -> this.keystorePassword = ca);
1✔
931
            stringProperty(props, PROP_TRUSTSTORE, s -> this.truststore = s);
1✔
932
            charArrayProperty(props, PROP_TRUSTSTORE_PASSWORD, ca -> this.truststorePassword = ca);
1✔
933
            stringProperty(props, PROP_TLS_ALGORITHM, s -> this.tlsAlgorithm = s);
1✔
934

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

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

939
            booleanProperty(props, PROP_NORANDOMIZE, b -> this.noRandomize = b);
1✔
940
            booleanProperty(props, PROP_NO_RESOLVE_HOSTNAMES, b -> this.noResolveHostnames = b);
1✔
941
            booleanProperty(props, PROP_REPORT_NO_RESPONDERS, b -> this.reportNoResponders = b);
1✔
942

943
            stringProperty(props, PROP_CONNECTION_NAME, s -> this.connectionName = s);
1✔
944
            booleanProperty(props, PROP_VERBOSE, b -> this.verbose = b);
1✔
945
            booleanProperty(props, PROP_NO_ECHO, b -> this.noEcho = b);
1✔
946
            booleanProperty(props, PROP_NO_HEADERS, b -> this.noHeaders = b);
1✔
947
            booleanProperty(props, PROP_NO_NORESPONDERS, b -> this.noNoResponders = b);
1✔
948
            booleanProperty(props, PROP_CLIENT_SIDE_LIMIT_CHECKS, b -> this.clientSideLimitChecks = b);
1✔
949
            booleanProperty(props, PROP_UTF8_SUBJECTS, b -> this.supportUTF8Subjects = b);
1✔
950
            booleanProperty(props, PROP_PEDANTIC, b -> this.pedantic = b);
1✔
951

952
            intProperty(props, PROP_MAX_RECONNECT, DEFAULT_MAX_RECONNECT, i -> this.maxReconnect = i);
1✔
953
            durationProperty(props, PROP_RECONNECT_WAIT, DEFAULT_RECONNECT_WAIT, d -> this.reconnectWait = d);
1✔
954
            durationProperty(props, PROP_RECONNECT_JITTER, DEFAULT_RECONNECT_JITTER, d -> this.reconnectJitter = d);
1✔
955
            durationProperty(props, PROP_RECONNECT_JITTER_TLS, DEFAULT_RECONNECT_JITTER_TLS, d -> this.reconnectJitterTls = d);
1✔
956
            longProperty(props, PROP_RECONNECT_BUF_SIZE, DEFAULT_RECONNECT_BUF_SIZE, l -> this.reconnectBufferSize = l);
1✔
957
            durationProperty(props, PROP_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, d -> this.connectionTimeout = d);
1✔
958
            intProperty(props, PROP_SOCKET_READ_TIMEOUT_MS, -1, i -> this.socketReadTimeoutMillis = i);
1✔
959
            durationProperty(props, PROP_SOCKET_WRITE_TIMEOUT, DEFAULT_SOCKET_WRITE_TIMEOUT, d -> this.socketWriteTimeout = d);
1✔
960
            intProperty(props, PROP_SOCKET_SO_LINGER, -1, i -> socketSoLinger = i);
1✔
961
            intProperty(props, PROP_SOCKET_RECEIVE_BUFFER_SIZE, -1, i -> this.receiveBufferSize = i);
1✔
962
            intProperty(props, PROP_SOCKET_SEND_BUFFER_SIZE, -1, i -> this.sendBufferSize = i);
1✔
963

964
            intGtEqZeroProperty(props, PROP_MAX_CONTROL_LINE, DEFAULT_MAX_CONTROL_LINE, i -> this.maxControlLine = i);
1✔
965
            durationProperty(props, PROP_PING_INTERVAL, DEFAULT_PING_INTERVAL, d -> this.pingInterval = d);
1✔
966
            durationProperty(props, PROP_CLEANUP_INTERVAL, DEFAULT_REQUEST_CLEANUP_INTERVAL, d -> this.requestCleanupInterval = d);
1✔
967
            intProperty(props, PROP_MAX_PINGS, DEFAULT_MAX_PINGS_OUT, i -> this.maxPingsOut = i);
1✔
968
            booleanProperty(props, PROP_USE_OLD_REQUEST_STYLE, b -> this.useOldRequestStyle = b);
1✔
969

970
            classnameProperty(props, PROP_ERROR_LISTENER, o -> this.errorListener = (ErrorListener) o);
1✔
971
            classnameProperty(props, PROP_TIME_TRACE_LOGGER, o -> this.timeTraceLogger = (TimeTraceLogger) o);
1✔
972
            classnameProperty(props, PROP_CONNECTION_CB, o -> this.connectionListener = (ConnectionListener) o);
1✔
973
            classnameProperty(props, PROP_READ_LISTENER_CLASS, o -> this.readListener = (ReadListener) o);
1✔
974
            classnameProperty(props, PROP_STATISTICS_COLLECTOR, o -> this.statisticsCollector = (StatisticsCollector) o);
1✔
975

976
            stringProperty(props, PROP_DATA_PORT_TYPE, s -> this.dataPortType = s);
1✔
977
            stringProperty(props, PROP_INBOX_PREFIX, this::inboxPrefix);
1✔
978
            intGtEqZeroProperty(props, PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE, DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE, i -> this.maxMessagesInOutgoingQueue = i);
1✔
979
            booleanProperty(props, PROP_DISCARD_MESSAGES_WHEN_OUTGOING_QUEUE_FULL, b -> this.discardMessagesWhenOutgoingQueueFull = b);
1✔
980

981
            booleanProperty(props, PROP_IGNORE_DISCOVERED_SERVERS, b -> this.ignoreDiscoveredServers = b);
1✔
982
            booleanProperty(props, PROP_TLS_FIRST, b -> this.tlsFirst = b);
1✔
983
            booleanProperty(props, PROP_USE_TIMEOUT_EXCEPTION, b -> this.useTimeoutException = b);
1✔
984
            booleanProperty(props, PROP_USE_DISPATCHER_WITH_EXECUTOR, b -> this.useDispatcherWithExecutor = b);
1✔
985
            booleanProperty(props, PROP_FORCE_FLUSH_ON_REQUEST, b -> this.forceFlushOnRequest = b);
1✔
986
            booleanProperty(props, PROP_FAST_FALLBACK, b -> this.enableFastFallback = b);
1✔
987

988
            classnameProperty(props, PROP_SERVERS_POOL_IMPLEMENTATION_CLASS, o -> this.serverPool = (ServerPool) o);
1✔
989
            classnameProperty(props, PROP_DISPATCHER_FACTORY_CLASS, o -> this.dispatcherFactory = (DispatcherFactory) o);
1✔
990
            classnameProperty(props, PROP_EXECUTOR_SERVICE_CLASS, o -> this.executor = (ExecutorService) o);
1✔
991
            classnameProperty(props, PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS, o -> this.scheduledExecutor = (ScheduledExecutorService) o);
1✔
992
            classnameProperty(props, PROP_CONNECT_THREAD_FACTORY_CLASS, o -> this.connectThreadFactory = (ThreadFactory) o);
1✔
993
            classnameProperty(props, PROP_CALLBACK_THREAD_FACTORY_CLASS, o -> this.callbackThreadFactory = (ThreadFactory) o);
1✔
994
            return this;
1✔
995
        }
996

997
        /**
998
         * Add a server to the list of known servers.
999
         *
1000
         * @param serverURL the URL for the server to add
1001
         * @throws IllegalArgumentException if the url is not formatted correctly.
1002
         * @return the Builder for chaining
1003
         */
1004
        public Builder server(String serverURL) {
1005
            return servers(serverURL.trim().split(","));
1✔
1006
        }
1007

1008
        /**
1009
         * Add an array of servers to the list of known servers.
1010
         *
1011
         * @param servers A list of server URIs
1012
         * @throws IllegalArgumentException if any url is not formatted correctly.
1013
         * @return the Builder for chaining
1014
         */
1015
        public Builder servers(String[] servers) {
1016
            for (String s : servers) {
1✔
1017
                if (s != null && !s.isEmpty()) {
1✔
1018
                    try {
1019
                        String unprocessed = s.trim();
1✔
1020
                        NatsUri nuri = new NatsUri(unprocessed);
1✔
1021
                        if (!natsServerUris.contains(nuri)) {
1✔
1022
                            natsServerUris.add(nuri);
1✔
1023
                            unprocessedServers.add(unprocessed);
1✔
1024
                        }
1025
                    }
1026
                    catch (URISyntaxException e) {
1✔
1027
                        throw new IllegalArgumentException(e);
1✔
1028
                    }
1✔
1029
                }
1030
            }
1031
            return this;
1✔
1032
        }
1033

1034
        /**
1035
         * Turn on the old request style that uses a new inbox and subscriber for each
1036
         * request.
1037
         * @return the Builder for chaining
1038
         */
1039
        public Builder oldRequestStyle() {
1040
            this.useOldRequestStyle = true;
1✔
1041
            return this;
1✔
1042
        }
1043

1044
        /**
1045
         * For the default server list provider, turn off server pool randomization.
1046
         * The default provider will pick servers from its list randomly on a reconnect.
1047
         * When noRandomize is set to true the default provider supplies a list that
1048
         * first contains servers as configured and then contains the servers as sent
1049
         * from the connected server.
1050
         * @return the Builder for chaining
1051
         */
1052
        public Builder noRandomize() {
1053
            this.noRandomize = true;
1✔
1054
            return this;
1✔
1055
        }
1056

1057
        /**
1058
         * For the default server list provider, whether to resolve hostnames when building server list.
1059
         * @return the Builder for chaining
1060
         */
1061
        public Builder noResolveHostnames() {
1062
            this.noResolveHostnames = true;
1✔
1063
            return this;
1✔
1064
        }
1065

1066
        public Builder reportNoResponders() {
1067
            this.reportNoResponders = true;
1✔
1068
            return this;
1✔
1069
        }
1070

1071
        /**
1072
         * Turn off echo. If supported by the nats-server version you are connecting to this
1073
         * flag will prevent the server from echoing messages back to the connection if it
1074
         * has subscriptions on the subject being published to.
1075
         * @return the Builder for chaining
1076
         */
1077
        public Builder noEcho() {
1078
            this.noEcho = true;
1✔
1079
            return this;
1✔
1080
        }
1081

1082
        /**
1083
         * Turn off header support. Some versions of the server don't support it.
1084
         * It's also not required if you don't use headers
1085
         * @return the Builder for chaining
1086
         */
1087
        public Builder noHeaders() {
1088
            this.noHeaders = true;
1✔
1089
            return this;
1✔
1090
        }
1091

1092
        /**
1093
         * Turn off noresponder support. Some versions of the server don't support it.
1094
         * @return the Builder for chaining
1095
         */
1096
        public Builder noNoResponders() {
1097
            this.noNoResponders = true;
1✔
1098
            return this;
1✔
1099
        }
1100

1101
        /**
1102
         * Set client side limit checks. Default is true
1103
         * @param checks the checks flag
1104
         * @return the Builder for chaining
1105
         */
1106
        public Builder clientSideLimitChecks(boolean checks) {
1107
            this.clientSideLimitChecks = checks;
1✔
1108
            return this;
1✔
1109
        }
1110

1111
        /**
1112
         * The client protocol is not clear about the encoding for subject names. For
1113
         * performance reasons, the Java client defaults to ASCII. You can enable UTF8
1114
         * with this method. The server, written in go, treats byte to string as UTF8 by default
1115
         * and should allow UTF8 subjects, but make sure to test any clients when using them.
1116
         * @return the Builder for chaining
1117
         */
1118
        public Builder supportUTF8Subjects() {
1119
            this.supportUTF8Subjects = true;
1✔
1120
            return this;
1✔
1121
        }
1122

1123
        /**
1124
         * Set the connection's optional Name.
1125
         *
1126
         * @param name the connections new name.
1127
         * @return the Builder for chaining
1128
         */
1129
        public Builder connectionName(String name) {
1130
            this.connectionName = name;
1✔
1131
            return this;
1✔
1132
        }
1133

1134
        /**
1135
         * Set the connection's inbox prefix. All inboxes will start with this string.
1136
         *
1137
         * @param prefix prefix to use.
1138
         * @return the Builder for chaining
1139
         */
1140
        public Builder inboxPrefix(String prefix) {
1141
            this.inboxPrefix = prefix;
1✔
1142

1143
            if (!this.inboxPrefix.endsWith(".")) {
1✔
1144
                this.inboxPrefix = this.inboxPrefix + ".";
1✔
1145
            }
1146
            return this;
1✔
1147
        }
1148

1149
        /**
1150
         * Turn on verbose mode with the server.
1151
         * @return the Builder for chaining
1152
         */
1153
        public Builder verbose() {
1154
            this.verbose = true;
1✔
1155
            return this;
1✔
1156
        }
1157

1158
        /**
1159
         * Turn on pedantic mode for the server, in relation to this connection.
1160
         * @return the Builder for chaining
1161
         */
1162
        public Builder pedantic() {
1163
            this.pedantic = true;
1✔
1164
            return this;
1✔
1165
        }
1166

1167
        /**
1168
         * Turn on advanced stats, primarily for test/benchmarks. These are visible if you
1169
         * call toString on the {@link Statistics Statistics} object.
1170
         * @return the Builder for chaining
1171
         */
1172
        public Builder turnOnAdvancedStats() {
1173
            this.trackAdvancedStats = true;
1✔
1174
            return this;
1✔
1175
        }
1176

1177
        /**
1178
         * Enable connection trace messages. Messages are printed to standard out. This option is for very
1179
         * fine-grained debugging of connection issues.
1180
         * @return the Builder for chaining
1181
         */
1182
        public Builder traceConnection() {
1183
            this.traceConnection = true;
1✔
1184
            return this;
1✔
1185
        }
1186

1187
        /**
1188
         * Sets the options to use the default SSL Context, if it exists.
1189
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1190
         * @return the Builder for chaining
1191
         */
1192
        public Builder secure() throws NoSuchAlgorithmException {
1193
            useDefaultTls = true;
1✔
1194
            return this;
1✔
1195
        }
1196

1197
        /**
1198
         * Set the options to use an SSL context that accepts any server certificate and has no client certificates.
1199
         * @throws NoSuchAlgorithmException <em>Not thrown, deferred to build() method, left in for backward compatibility</em>
1200
         * @return the Builder for chaining
1201
         */
1202
        public Builder opentls() throws NoSuchAlgorithmException {
1203
            useTrustAllTls = true;
1✔
1204
            return this;
1✔
1205
        }
1206

1207
        /**
1208
         * Set the SSL context, requires that the server supports TLS connections and
1209
         * the URI specifies TLS.
1210
         * If provided, the context takes precedence over any other TLS/SSL properties
1211
         * set in the builder, including the sslContextFactory
1212
         * @param ctx the SSL Context to use for TLS connections
1213
         * @return the Builder for chaining
1214
         */
1215
        public Builder sslContext(SSLContext ctx) {
1216
            this.sslContext = ctx;
1✔
1217
            return this;
1✔
1218
        }
1219

1220
        /**
1221
         * Set the factory that provides the ssl context. The factory is superseded
1222
         * by an instance of SSLContext
1223
         * @param sslContextFactory the SSL Context for use to create a ssl context
1224
         * @return the Builder for chaining
1225
         */
1226
        public Builder sslContextFactory(SSLContextFactory sslContextFactory) {
1227
            this.sslContextFactory = sslContextFactory;
1✔
1228
            return this;
1✔
1229
        }
1230

1231
        /**
1232
         *
1233
         * @param keystore the path to the keystore file
1234
         * @return the Builder for chaining
1235
         */
1236
        public Builder keystorePath(String keystore) {
1237
            this.keystore = emptyAsNull(keystore);
1✔
1238
            return this;
1✔
1239
        }
1240

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

1251
        /**
1252
         *
1253
         * @param truststore the path to the trust store file
1254
         * @return the Builder for chaining
1255
         */
1256
        public Builder truststorePath(String truststore) {
1257
            this.truststore = emptyAsNull(truststore);
1✔
1258
            return this;
1✔
1259
        }
1260

1261
        /**
1262
         *
1263
         * @param truststorePassword the password for the trust store
1264
         * @return the Builder for chaining
1265
         */
1266
        public Builder truststorePassword(char[] truststorePassword) {
1267
            this.truststorePassword = truststorePassword == null || truststorePassword.length == 0 ? null : truststorePassword;
1✔
1268
            return this;
1✔
1269
        }
1270

1271
        /**
1272
         *
1273
         * @param tlsAlgorithm the tls algorithm. Default is {@value SSLUtils#DEFAULT_TLS_ALGORITHM}
1274
         * @return the Builder for chaining
1275
         */
1276
        public Builder tlsAlgorithm(String tlsAlgorithm) {
1277
            this.tlsAlgorithm = emptyOrNullAs(tlsAlgorithm, DEFAULT_TLS_ALGORITHM);
1✔
1278
            return this;
1✔
1279
        }
1280

1281
        /**
1282
         *
1283
         * @param credentialPath the path to the credentials file for creating an {@link AuthHandler AuthHandler}
1284
         * @return the Builder for chaining
1285
         */
1286
        public Builder credentialPath(String credentialPath) {
1287
            this.credentialPath = emptyAsNull(credentialPath);
1✔
1288
            return this;
1✔
1289
        }
1290

1291
        /**
1292
         * Equivalent to calling maxReconnects with 0, {@link #maxReconnects(int) maxReconnects}.
1293
         * @return the Builder for chaining
1294
         */
1295
        public Builder noReconnect() {
1296
            this.maxReconnect = 0;
1✔
1297
            return this;
1✔
1298
        }
1299

1300
        /**
1301
         * Set the maximum number of reconnect attempts. Use 0 to turn off
1302
         * auto-reconnect. Use -1 to turn on infinite reconnects.
1303
         *
1304
         * <p>The reconnect count is incremented on a per-server basis, so if the server list contains 5 servers
1305
         * but max reconnects is set to 3, only 3 of those servers will be tried.</p>
1306
         *
1307
         * <p>This library has a slight difference from some NATS clients, if you set the maxReconnects to zero
1308
         * there will not be any reconnect attempts, regardless of the number of known servers.</p>
1309
         *
1310
         * <p>The reconnect state is entered when the connection is connected and loses
1311
         * that connection. During the initial connection attempt, the client will cycle over
1312
         * its server list one time, regardless of what maxReconnects is set to. The only exception
1313
         * to this is the async connect method {@link Nats#connectAsynchronously(Options, boolean) connectAsynchronously}.</p>
1314
         *
1315
         * @param max the maximum reconnect attempts
1316
         * @return the Builder for chaining
1317
         */
1318
        public Builder maxReconnects(int max) {
1319
            this.maxReconnect = max;
1✔
1320
            return this;
1✔
1321
        }
1322

1323
        /**
1324
         * Set the time to wait between reconnect attempts to the same server. This setting is only used
1325
         * by the client when the same server appears twice in the reconnect attempts, either because it is the
1326
         * only known server or by random chance. Note, the randomization of the server list doesn't occur per
1327
         * attempt, it is performed once at the start, so if there are 2 servers in the list you will never encounter
1328
         * the reconnect wait.
1329
         *
1330
         * @param time the time to wait
1331
         * @return the Builder for chaining
1332
         */
1333
        public Builder reconnectWait(Duration time) {
1334
            this.reconnectWait = time;
1✔
1335
            return this;
1✔
1336
        }
1337

1338
        /**
1339
         * Set the jitter time to wait between reconnect attempts to the same server. This setting is used to vary
1340
         * the reconnect wait to avoid multiple clients trying to reconnect to servers at the same time.
1341
         *
1342
         * @param time the time to wait
1343
         * @return the Builder for chaining
1344
         */
1345
        public Builder reconnectJitter(Duration time) {
1346
            this.reconnectJitter = time;
1✔
1347
            return this;
1✔
1348
        }
1349

1350
        /**
1351
         * Set the jitter time for a tls/secure connection to wait between reconnect attempts to the same server.
1352
         * This setting is used to vary the reconnect wait to avoid multiple clients trying to reconnect to
1353
         * servers at the same time.
1354
         *
1355
         * @param time the time to wait
1356
         * @return the Builder for chaining
1357
         */
1358
        public Builder reconnectJitterTls(Duration time) {
1359
            this.reconnectJitterTls = time;
1✔
1360
            return this;
1✔
1361
        }
1362

1363
        /**
1364
         * Set the maximum length of a control line sent by this connection. This value is also configured
1365
         * in the server but the protocol doesn't currently forward that setting. Configure it here so that
1366
         * the client can ensure that messages are valid before sending to the server.
1367
         *
1368
         * @param bytes the max byte count
1369
         * @return the Builder for chaining
1370
         */
1371
        public Builder maxControlLine(int bytes) {
1372
            this.maxControlLine = bytes < 0 ? DEFAULT_MAX_CONTROL_LINE : bytes;
1✔
1373
            return this;
1✔
1374
        }
1375

1376
        /**
1377
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1378
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1379
         *
1380
         * @param connectionTimeout the time to wait
1381
         * @return the Builder for chaining
1382
         */
1383
        public Builder connectionTimeout(Duration connectionTimeout) {
1384
            this.connectionTimeout = connectionTimeout;
1✔
1385
            return this;
1✔
1386
        }
1387

1388
        /**
1389
         * Set the timeout for connection attempts. Each server in the options is allowed this timeout
1390
         * so if 3 servers are tried with a timeout of 5s the total time could be 15s.
1391
         *
1392
         * @param connectionTimeoutMillis the time to wait in milliseconds
1393
         * @return the Builder for chaining
1394
         */
1395
        public Builder connectionTimeout(long connectionTimeoutMillis) {
1396
            this.connectionTimeout = Duration.ofMillis(connectionTimeoutMillis);
×
1397
            return this;
×
1398
        }
1399

1400
        /**
1401
         * Set the timeout to use around socket reads
1402
         * @param socketReadTimeoutMillis the timeout milliseconds
1403
         * @return the Builder for chaining
1404
         */
1405
        public Builder socketReadTimeoutMillis(int socketReadTimeoutMillis) {
1406
            this.socketReadTimeoutMillis = socketReadTimeoutMillis;
×
1407
            return this;
×
1408
        }
1409

1410
        /**
1411
         * Set the timeout to use around socket writes
1412
         * @param socketWriteTimeoutMillis the timeout milliseconds
1413
         * @return the Builder for chaining
1414
         */
1415
        public Builder socketWriteTimeout(long socketWriteTimeoutMillis) {
1416
            socketWriteTimeout = Duration.ofMillis(socketWriteTimeoutMillis);
1✔
1417
            return this;
1✔
1418
        }
1419

1420
        /**
1421
         * Set the timeout to use around socket writes
1422
         * @param socketWriteTimeout the timeout duration
1423
         * @return the Builder for chaining
1424
         */
1425
        public Builder socketWriteTimeout(Duration socketWriteTimeout) {
1426
            this.socketWriteTimeout = socketWriteTimeout;
1✔
1427
            return this;
1✔
1428
        }
1429

1430
        /**
1431
         * Set the value of the socket SO LINGER property in seconds.
1432
         * This feature is used by library data port implementations.
1433
         * Setting this is a last resort if socket closes are a problem
1434
         * in your environment, otherwise it's generally not necessary
1435
         * to set this. The value must be greater than or equal to 0
1436
         * to have the code call socket.setSoLinger with true and the timeout value
1437
         * @param socketSoLinger the number of seconds to linger
1438
         * @return the Builder for chaining
1439
         */
1440
        public Builder socketSoLinger(int socketSoLinger) {
1441
            this.socketSoLinger = socketSoLinger;
×
1442
            return this;
×
1443
        }
1444

1445
        /**
1446
         * Set the value of the socket SO_RCVBUF property in bytes
1447
         * The SO_RCVBUF option is used by the platform's networking code as a hint for the size to set the underlying network I/O buffers.
1448
         * MAY OVERRIDE THE UNDERLYING JAVA SOCKET IMPLEMENTATION - USE AT YOUR OWN RISK
1449
         * @param receiveBufferSize the size in bytes
1450
         * @return the Builder for chaining
1451
         */
1452
        public Builder receiveBufferSize(int receiveBufferSize) {
NEW
1453
            this.receiveBufferSize = receiveBufferSize;
×
NEW
1454
            return this;
×
1455
        }
1456

1457
        /**
1458
         * Set the value of the socket SO_SNDBUF property in bytes
1459
         * The SO_SNDBUF option is used by the platform's networking code as a hint for the size to set the underlying network I/O buffers.
1460
         * MAY OVERRIDE THE UNDERLYING JAVA SOCKET IMPLEMENTATION - USE AT YOUR OWN RISK
1461
         * @param sendBufferSize the size in bytes
1462
         * @return the Builder for chaining
1463
         */
1464
        public Builder sendBufferSize(int sendBufferSize) {
NEW
1465
            this.sendBufferSize = sendBufferSize;
×
NEW
1466
            return this;
×
1467
        }
1468

1469
        /**
1470
         * Set the interval between attempts to pings the server. These pings are automated,
1471
         * and capped by {@link #maxPingsOut(int) maxPingsOut()}. As of 2.4.4 the library
1472
         * may wait up to 2 * time to send a ping. Incoming traffic from the server can postpone
1473
         * the next ping to avoid pings taking up bandwidth during busy messaging.
1474
         * Keep in mind that a ping requires a round trip to the server. Setting this value to a small
1475
         * number can result in quick failures due to maxPingsOut being reached, these failures will
1476
         * force a disconnect/reconnect which can result in messages being held back or failed. In general,
1477
         * the ping interval should be set in seconds but this value is not enforced as it would result in
1478
         * an API change from the 2.0 release.
1479
         *
1480
         * @param time the time between client to server pings
1481
         * @return the Builder for chaining
1482
         */
1483
        public Builder pingInterval(Duration time) {
1484
            this.pingInterval = time == null ? DEFAULT_PING_INTERVAL : time;
1✔
1485
            return this;
1✔
1486
        }
1487

1488
        /**
1489
         * Set the interval between cleaning passes on outstanding request futures that are cancelled or timeout
1490
         * in the application code.
1491
         *
1492
         * <p>The default value is probably reasonable, but this interval is useful in a very noisy network
1493
         * situation where lots of requests are used.
1494
         *
1495
         * @param time the cleaning interval
1496
         * @return the Builder for chaining
1497
         */
1498
        public Builder requestCleanupInterval(Duration time) {
1499
            this.requestCleanupInterval = time;
1✔
1500
            return this;
1✔
1501
        }
1502

1503
        /**
1504
         * Set the maximum number of pings the client can have in flight.
1505
         *
1506
         * @param max the max pings
1507
         * @return the Builder for chaining
1508
         */
1509
        public Builder maxPingsOut(int max) {
1510
            this.maxPingsOut = max;
1✔
1511
            return this;
1✔
1512
        }
1513

1514
        /**
1515
         * Sets the initial size for buffers in the connection, primarily for testing.
1516
         * @param size the size in bytes to make buffers for connections created with this options
1517
         * @return the Builder for chaining
1518
         */
1519
        public Builder bufferSize(int size) {
1520
            this.bufferSize = size;
1✔
1521
            return this;
1✔
1522
        }
1523

1524
        /**
1525
         * Set the maximum number of bytes to buffer in the client when trying to
1526
         * reconnect. When this value is exceeded the client will start to drop messages.
1527
         * The count of dropped messages can be read from the {@link Statistics#getDroppedCount() Statistics}.
1528
         * A value of zero will disable the reconnect buffer, a value less than zero means unlimited. Caution
1529
         * should be used for negative numbers as they can result in an unreliable network connection plus a
1530
         * high message rate leading to an out of memory error.
1531
         *
1532
         * @param size the size in bytes
1533
         * @return the Builder for chaining
1534
         */
1535
        public Builder reconnectBufferSize(long size) {
1536
            this.reconnectBufferSize = size;
1✔
1537
            return this;
1✔
1538
        }
1539

1540
        /**
1541
         * Set the username and password for basic authentication.
1542
         * If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
1543
         * these values can be used as a fallback.
1544
         * use the char[] version instead for better security
1545
         *
1546
         * @param userName a non-empty userName
1547
         * @param password the password, in plain text
1548
         * @return the Builder for chaining
1549
         */
1550
        public Builder userInfo(String userName, String password) {
1551
            this.username = userName.toCharArray();
1✔
1552
            this.password = password.toCharArray();
1✔
1553
            return this;
1✔
1554
        }
1555

1556
        /**
1557
         * Set the username and password for basic authentication.
1558
         * If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
1559
         * these values can be used as a fallback.
1560
         *
1561
         * @param userName a non-empty userName
1562
         * @param password the password, in plain text
1563
         * @return the Builder for chaining
1564
         */
1565
        public Builder userInfo(char[] userName, char[] password) {
1566
            this.username = userName;
1✔
1567
            this.password = password;
1✔
1568
            return this;
1✔
1569
        }
1570

1571
        /**
1572
         * Set the token for token-based authentication.
1573
         * If a token is provided in a server URI, it overrides this value.
1574
         *
1575
         * @param token The token
1576
         * @return the Builder for chaining
1577
         * @deprecated use the char[] version instead for better security
1578
         */
1579
        @Deprecated
1580
        public Builder token(String token) {
1581
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1582
            return this;
1✔
1583
        }
1584

1585
        /**
1586
         * Set the token for token-based authentication.
1587
         * If a token is provided in a server URI, it overrides this value.
1588
         *
1589
         * @param token The token
1590
         * @return the Builder for chaining
1591
         */
1592
        public Builder token(char[] token) {
1593
            this.tokenSupplier = new DefaultTokenSupplier(token);
1✔
1594
            return this;
1✔
1595
        }
1596

1597
        /**
1598
         * Set the token supplier for token-based authentication.
1599
         * If a token is provided in a server URI, it overrides this value.
1600
         *
1601
         * @param tokenSupplier The tokenSupplier
1602
         * @return the Builder for chaining
1603
         */
1604
        public Builder tokenSupplier(Supplier<char[]> tokenSupplier) {
1605
            this.tokenSupplier = tokenSupplier == null ? new DefaultTokenSupplier() : tokenSupplier;
1✔
1606
            return this;
1✔
1607
        }
1608

1609
        /**
1610
         * Set the {@link AuthHandler AuthHandler} to sign the server nonce for authentication in
1611
         * nonce-mode.
1612
         *
1613
         * @param handler The new AuthHandler for this connection.
1614
         * @return the Builder for chaining
1615
         */
1616
        public Builder authHandler(AuthHandler handler) {
1617
            this.authHandler = handler;
1✔
1618
            return this;
1✔
1619
        }
1620

1621
        /**
1622
         * Set the {@link ReconnectDelayHandler ReconnectDelayHandler} for custom reconnect duration
1623
         *
1624
         * @param handler The new ReconnectDelayHandler for this connection.
1625
         * @return the Builder for chaining
1626
         */
1627
        public Builder reconnectDelayHandler(ReconnectDelayHandler handler) {
1628
            this.reconnectDelayHandler = handler;
1✔
1629
            return this;
1✔
1630
        }
1631

1632
        /**
1633
         * Set the {@link ErrorListener ErrorListener} to receive asynchronous error events related to this
1634
         * connection.
1635
         *
1636
         * @param listener The new ErrorListener for this connection.
1637
         * @return the Builder for chaining
1638
         */
1639
        public Builder errorListener(ErrorListener listener) {
1640
            this.errorListener = listener;
1✔
1641
            return this;
1✔
1642
        }
1643

1644
        /**
1645
         * Set the {@link TimeTraceLogger TimeTraceLogger} to receive trace events related to this connection.
1646
         * @param logger The new TimeTraceLogger for this connection.
1647
         * @return the Builder for chaining
1648
         */
1649
        public Builder timeTraceLogger(TimeTraceLogger logger) {
1650
            this.timeTraceLogger = logger;
1✔
1651
            return this;
1✔
1652
        }
1653

1654
        /**
1655
         * Set the {@link ConnectionListener ConnectionListener} to receive asynchronous notifications of disconnect
1656
         * events.
1657
         *
1658
         * @param listener The new ConnectionListener for this type of event.
1659
         * @return the Builder for chaining
1660
         */
1661
        public Builder connectionListener(ConnectionListener listener) {
1662
            this.connectionListener = listener;
1✔
1663
            return this;
1✔
1664
        }
1665

1666
        /**
1667
         * Sets a listener to be notified on incoming protocol/message
1668
         *
1669
         * @param readListener the listener
1670
         * @return the Builder for chaining
1671
         */
1672
        public Builder readListener(ReadListener readListener) {
1673
            this.readListener = readListener;
×
1674
            return this;
×
1675
        }
1676

1677
        /**
1678
         * Set the {@link StatisticsCollector StatisticsCollector} to collect connection metrics.
1679
         * <p>
1680
         * If not set, then a default implementation will be used.
1681
         *
1682
         * @param collector the new StatisticsCollector for this connection.
1683
         * @return the Builder for chaining
1684
         */
1685
        public Builder statisticsCollector(StatisticsCollector collector) {
1686
            this.statisticsCollector = collector;
×
1687
            return this;
×
1688
        }
1689

1690
        /**
1691
         * Set the {@link ExecutorService ExecutorService} used to run threaded tasks. The default is a
1692
         * cached thread pool that names threads after the connection name (or a default). This executor
1693
         * is used for reading and writing the underlying sockets as well as for each Dispatcher.
1694
         * The default executor uses a short keepalive time, 500ms, to insure quick shutdowns. This is reasonable
1695
         * since most threads from the executor are long-lived. If you customize, be sure to keep the shutdown
1696
         * effect in mind, executors can block for their keepalive time. The default executor also marks threads
1697
         * with priority normal and as non-daemon.
1698
         *
1699
         * @param executor The ExecutorService to use for connections built with these options.
1700
         * @return the Builder for chaining
1701
         */
1702
        public Builder executor(ExecutorService executor) {
1703
            this.executor = executor;
1✔
1704
            return this;
1✔
1705
        }
1706

1707
        /**
1708
         * Set the {@link ScheduledExecutorService ScheduledExecutorService} used to run scheduled task like
1709
         * heartbeat timers
1710
         * The default is a ScheduledThreadPoolExecutor that does not
1711
         *  execute delayed tasks after shutdown and removes tasks on cancel;
1712
         * @param scheduledExecutor The ScheduledExecutorService to use for timer tasks
1713
         * @return the Builder for chaining
1714
         */
1715
        public Builder scheduledExecutor(ScheduledExecutorService scheduledExecutor) {
1716
            this.scheduledExecutor = scheduledExecutor;
1✔
1717
            return this;
1✔
1718
        }
1719

1720
        /**
1721
         * Sets custom thread factory for the executor service
1722
         *
1723
         * @param threadFactory the thread factory to use for the executor service
1724
         * @return the Builder for chaining
1725
         */
1726
        public Builder connectThreadFactory(ThreadFactory threadFactory) {
1727
            this.connectThreadFactory = threadFactory;
1✔
1728
            return this;
1✔
1729
        }
1730

1731
        /**
1732
         * Sets custom thread factory for the executor service
1733
         *
1734
         * @param threadFactory the thread factory to use for the executor service
1735
         * @return the Builder for chaining
1736
         */
1737
        public Builder callbackThreadFactory(ThreadFactory threadFactory) {
1738
            this.callbackThreadFactory = threadFactory;
1✔
1739
            return this;
1✔
1740
        }
1741

1742
        /**
1743
         * Add an HttpRequest interceptor which can be used to modify the HTTP request when using websockets
1744
         *
1745
         * @param interceptor The interceptor
1746
         * @return the Builder for chaining
1747
         */
1748
        public Builder httpRequestInterceptor(java.util.function.Consumer<HttpRequest> interceptor) {
1749
            if (null == this.httpRequestInterceptors) {
1✔
1750
                this.httpRequestInterceptors = new ArrayList<>();
1✔
1751
            }
1752
            this.httpRequestInterceptors.add(interceptor);
1✔
1753
            return this;
1✔
1754
        }
1755

1756
        /**
1757
         * Overwrite the list of HttpRequest interceptors which can be used to modify the HTTP request when using websockets
1758
         *
1759
         * @param interceptors The list of interceptors
1760
         * @return the Builder for chaining
1761
         */
1762
        public Builder httpRequestInterceptors(Collection<? extends java.util.function.Consumer<HttpRequest>> interceptors) {
1763
            this.httpRequestInterceptors = new ArrayList<>(interceptors);
1✔
1764
            return this;
1✔
1765
        }
1766

1767
        /**
1768
         * Define a proxy to use when connecting.
1769
         *
1770
         * @param proxy is the HTTP or socks proxy to use.
1771
         * @return the Builder for chaining
1772
         */
1773
        public Builder proxy(Proxy proxy) {
1774
            this.proxy = proxy;
1✔
1775
            return this;
1✔
1776
        }
1777

1778
        /**
1779
         * The class to use for this connections data port. This is an advanced setting
1780
         * and primarily useful for testing.
1781
         *
1782
         * @param dataPortClassName a valid and accessible class name
1783
         * @return the Builder for chaining
1784
         */
1785
        public Builder dataPortType(String dataPortClassName) {
1786
            this.dataPortType = dataPortClassName == null ? DEFAULT_DATA_PORT_TYPE : dataPortClassName;
1✔
1787
            return this;
1✔
1788
        }
1789

1790
        /**
1791
         * Set the maximum number of messages in the outgoing queue.
1792
         *
1793
         * @param maxMessagesInOutgoingQueue the maximum number of messages in the outgoing queue
1794
         * @return the Builder for chaining
1795
         */
1796
        public Builder maxMessagesInOutgoingQueue(int maxMessagesInOutgoingQueue) {
1797
            this.maxMessagesInOutgoingQueue = maxMessagesInOutgoingQueue < 0
1✔
1798
                ? DEFAULT_MAX_MESSAGES_IN_OUTGOING_QUEUE
1799
                : maxMessagesInOutgoingQueue;
1800
            return this;
1✔
1801
        }
1802

1803
        /**
1804
         * Enable discard messages when the outgoing queue full. See {@link Builder#maxMessagesInOutgoingQueue(int) maxMessagesInOutgoingQueue}
1805
         *
1806
         * @return the Builder for chaining
1807
         */
1808
        public Builder discardMessagesWhenOutgoingQueueFull() {
1809
            this.discardMessagesWhenOutgoingQueueFull = true;
1✔
1810
            return this;
1✔
1811
        }
1812

1813
        /**
1814
         * Turn off use of discovered servers when connecting / reconnecting. Used in the default server list provider.
1815
         * @return the Builder for chaining
1816
         */
1817
        public Builder ignoreDiscoveredServers() {
1818
            this.ignoreDiscoveredServers = true;
1✔
1819
            return this;
1✔
1820
        }
1821

1822
        /**
1823
         * Set TLS Handshake First behavior on. Default is off.
1824
         * TLS Handshake First is used to instruct the library perform
1825
         * the TLS handshake right after the connect and before receiving
1826
         * the INFO protocol from the server. If this option is enabled
1827
         * but the server is not configured to perform the TLS handshake
1828
         * first, the connection will fail.
1829
         * @return the Builder for chaining
1830
         */
1831
        public Builder tlsFirst() {
1832
            this.tlsFirst = true;
1✔
1833
            return this;
1✔
1834
        }
1835

1836
        /**
1837
         * Throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}?
1838
         * @return the Builder for chaining
1839
         */
1840
        public Builder useTimeoutException() {
1841
            this.useTimeoutException = true;
×
1842
            return this;
×
1843
        }
1844

1845
        /**
1846
         * Instruct dispatchers to dispatch all messages as a task, instead of directly from dispatcher thread
1847
         * @return the Builder for chaining
1848
         */
1849
        public Builder useDispatcherWithExecutor() {
1850
            this.useDispatcherWithExecutor = true;
1✔
1851
            return this;
1✔
1852
        }
1853

1854
        /**
1855
         * Instruct requests to turn off flush on requests.
1856
         * @return the Builder for chaining
1857
         */
1858
        public Builder dontForceFlushOnRequest() {
1859
            this.forceFlushOnRequest = false;
×
1860
            return this;
×
1861
        }
1862

1863
        /**
1864
         * Set the ServerPool implementation for connections to use instead of the default implementation
1865
         * @param serverPool the implementation
1866
         * @return the Builder for chaining
1867
         */
1868
        public Builder serverPool(ServerPool serverPool) {
1869
            this.serverPool = serverPool;
1✔
1870
            return this;
1✔
1871
        }
1872

1873
        /**
1874
         * Set the DispatcherFactory implementation for connections to use instead of the default implementation
1875
         * @param dispatcherFactory the implementation
1876
         * @return the Builder for chaining
1877
         */
1878
        public Builder dispatcherFactory(DispatcherFactory dispatcherFactory) {
1879
            this.dispatcherFactory = dispatcherFactory;
×
1880
            return this;
×
1881
        }
1882

1883
        /**
1884
         * Whether to enable Fast fallback algorithm for socket connect
1885
         * @return the Builder for chaining
1886
         */
1887
        public Builder enableFastFallback() {
1888
            this.enableFastFallback = true;
1✔
1889
            return this;
1✔
1890
        }
1891

1892
        /**
1893
         * Build an Options object from this Builder.
1894
         *
1895
         * <p>If the Options builder was not provided with a server, a default one will be included
1896
         * {@link Options#DEFAULT_URL}. If only a single server URI is included, the builder
1897
         * will try a few things to make connecting easier:
1898
         * <ul>
1899
         * <li>If there is no user/password is set but the URI has them, {@code nats://user:password@server:port}, they will be used.
1900
         * <li>If there is no token is set but the URI has one, {@code nats://token@server:port}, it will be used.
1901
         * <li>If the URI is of the form tls:// and no SSL context was assigned, one is created, see {@link Options.Builder#secure() secure()}.
1902
         * <li>If the URI is of the form opentls:// and no SSL context was assigned one will be created
1903
         * that does not check the servers certificate for validity. This is not secure and only provided
1904
         * for tests and development.
1905
         * </ul>
1906
         *
1907
         * @return the new options object
1908
         * @throws IllegalStateException if there is a conflict in the options, like a token and a user/pass
1909
         */
1910
        public Options build() throws IllegalStateException {
1911
            // ----------------------------------------------------------------------------------------------------
1912
            // BUILD IMPL
1913
            // ----------------------------------------------------------------------------------------------------
1914
            if (this.username != null && tokenSupplier.get() != null) {
1✔
1915
                throw new IllegalStateException("Options can't have token and username");
1✔
1916
            }
1917

1918
            if (inboxPrefix == null) {
1✔
1919
                inboxPrefix = DEFAULT_INBOX_PREFIX;
×
1920
            }
1921

1922
            boolean checkUrisForSecure = true;
1✔
1923
            if (natsServerUris.isEmpty()) {
1✔
1924
                server(DEFAULT_URL);
1✔
1925
                checkUrisForSecure = false;
1✔
1926
            }
1927

1928
            // ssl context can be directly provided, but if it's not
1929
            // there might be a factory, or just see if we should make it ourselves
1930
            if (sslContext == null) {
1✔
1931
                if (sslContextFactory != null) {
1✔
1932
                    sslContext = sslContextFactory.createSSLContext(new SSLContextFactoryProperties.Builder()
1✔
1933
                        .keystore(keystore)
1✔
1934
                        .keystorePassword(keystorePassword)
1✔
1935
                        .truststore(truststore)
1✔
1936
                        .truststorePassword(truststorePassword)
1✔
1937
                        .tlsAlgorithm(tlsAlgorithm)
1✔
1938
                        .build());
1✔
1939
                }
1940
                else {
1941
                    if (keystore != null || truststore != null) {
1✔
1942
                        // the user provided keystore/truststore properties, the want us to make the sslContext that way
1943
                        try {
1944
                            sslContext = SSLUtils.createSSLContext(keystore, keystorePassword, truststore, truststorePassword, tlsAlgorithm);
1✔
1945
                        }
1946
                        catch (Exception e) {
×
1947
                            throw new IllegalStateException("Unable to create SSL context", e);
×
1948
                        }
1✔
1949
                    }
1950
                    else {
1951
                        // the sslContext has not been requested via factory or keystore/truststore properties
1952
                        // If we haven't been told to use the default or the trust all context
1953
                        // and the server isn't the default url, check to see if the server uris
1954
                        // suggest we need the ssl context.
1955
                        if (!useDefaultTls && !useTrustAllTls && checkUrisForSecure) {
1✔
1956
                            for (int i = 0; sslContext == null && i < natsServerUris.size(); i++) {
1✔
1957
                                NatsUri natsUri = natsServerUris.get(i);
1✔
1958
                                switch (natsUri.getScheme()) {
1✔
1959
                                    case TLS_PROTOCOL:
1960
                                    case SECURE_WEBSOCKET_PROTOCOL:
1961
                                        useDefaultTls = true;
1✔
1962
                                        break;
1✔
1963
                                    case OPENTLS_PROTOCOL:
1964
                                        useTrustAllTls = true;
1✔
1965
                                        break;
1966
                                }
1967
                            }
1968
                        }
1969

1970
                        // check trust all (open) first, in case they provided both
1971
                        // PROP_SECURE (secure) and PROP_OPENTLS (opentls)
1972
                        if (useTrustAllTls) {
1✔
1973
                            try {
1974
                                this.sslContext = SSLUtils.createTrustAllTlsContext();
1✔
1975
                            }
1976
                            catch (GeneralSecurityException e) {
×
1977
                                throw new IllegalStateException("Unable to create SSL context", e);
×
1978
                            }
1✔
1979
                        }
1980
                        else if (useDefaultTls) {
1✔
1981
                            try {
1982
                                this.sslContext = SSLContext.getDefault();
1✔
1983
                            }
1984
                            catch (NoSuchAlgorithmException e) {
×
1985
                                throw new IllegalStateException("Unable to create default SSL context", e);
×
1986
                            }
1✔
1987
                        }
1988
                    }
1989
                }
1990
            }
1991

1992
            if (tlsFirst && sslContext == null) {
1✔
1993
                throw new IllegalStateException("SSL context required for tls handshake first");
×
1994
            }
1995

1996
            if (credentialPath != null) {
1✔
1997
                File file = new File(credentialPath).getAbsoluteFile();
1✔
1998
                authHandler = Nats.credentials(file.toString());
1✔
1999
            }
2000

2001
            if (socketReadTimeoutMillis < 1) {
1✔
2002
                socketReadTimeoutMillis = 0; // just for consistency. The connection compares to gt 0
1✔
2003
            }
2004

2005
            if (socketWriteTimeout != null && socketWriteTimeout.toNanos() < MINIMUM_SOCKET_WRITE_TIMEOUT_NANOS) {
1✔
2006
                throw new IllegalArgumentException("Socket Write Timeout cannot be less than " + MINIMUM_SOCKET_WRITE_TIMEOUT_NANOS + " nanoseconds.");
×
2007
            }
2008

2009
            if (socketSoLinger < 1) {
1✔
2010
                socketSoLinger = -1;
1✔
2011
            }
2012

2013
            if (receiveBufferSize < 1) {
1✔
2014
                receiveBufferSize = -1;
1✔
2015
            }
2016

2017
            if (sendBufferSize < 1) {
1✔
2018
                sendBufferSize = -1;
1✔
2019
            }
2020

2021
            if (errorListener == null) {
1✔
2022
                errorListener = new ErrorListenerLoggerImpl();
1✔
2023
            }
2024

2025
            if (timeTraceLogger == null) {
1✔
2026
                if (traceConnection) {
1✔
2027
                    timeTraceLogger = (format, args) -> {
1✔
2028
                        String timeStr = DateTimeFormatter.ISO_TIME.format(LocalDateTime.now());
1✔
2029
                        System.out.println("[" + timeStr + "] connect trace: " + String.format(format, args));
1✔
2030
                    };
1✔
2031
                }
2032
                else {
2033
                    timeTraceLogger = (f, a) -> {};
1✔
2034
                }
2035
            }
2036
            else {
2037
                // if the dev provided an impl, we assume they meant to time trace the connection
2038
                traceConnection = true;
1✔
2039
            }
2040

2041
            return new Options(this);
1✔
2042
        }
2043

2044
        // ----------------------------------------------------------------------------------------------------
2045
        // BUILDER COPY CONSTRUCTOR
2046
        // ----------------------------------------------------------------------------------------------------
2047
        public Builder(Options o) {
1✔
2048
            if (o == null) {
1✔
2049
                throw new IllegalArgumentException("Options cannot be null");
1✔
2050
            }
2051

2052
            this.natsServerUris.addAll(o.natsServerUris);
1✔
2053
            this.unprocessedServers.addAll(o.unprocessedServers);
1✔
2054
            this.noRandomize = o.noRandomize;
1✔
2055
            this.noResolveHostnames = o.noResolveHostnames;
1✔
2056
            this.reportNoResponders = o.reportNoResponders;
1✔
2057
            this.connectionName = o.connectionName;
1✔
2058
            this.verbose = o.verbose;
1✔
2059
            this.pedantic = o.pedantic;
1✔
2060
            this.sslContext = o.sslContext;
1✔
2061
            this.maxReconnect = o.maxReconnect;
1✔
2062
            this.reconnectWait = o.reconnectWait;
1✔
2063
            this.reconnectJitter = o.reconnectJitter;
1✔
2064
            this.reconnectJitterTls = o.reconnectJitterTls;
1✔
2065
            this.connectionTimeout = o.connectionTimeout;
1✔
2066
            this.socketReadTimeoutMillis = o.socketReadTimeoutMillis;
1✔
2067
            this.socketWriteTimeout = o.socketWriteTimeout;
1✔
2068
            this.socketSoLinger = o.socketSoLinger;
1✔
2069
            this.receiveBufferSize = o.receiveBufferSize;
1✔
2070
            this.sendBufferSize = o.sendBufferSize;
1✔
2071
            this.pingInterval = o.pingInterval;
1✔
2072
            this.requestCleanupInterval = o.requestCleanupInterval;
1✔
2073
            this.maxPingsOut = o.maxPingsOut;
1✔
2074
            this.reconnectBufferSize = o.reconnectBufferSize;
1✔
2075
            this.username = o.username;
1✔
2076
            this.password = o.password;
1✔
2077
            this.tokenSupplier = o.tokenSupplier;
1✔
2078
            this.useOldRequestStyle = o.useOldRequestStyle;
1✔
2079
            this.maxControlLine = o.maxControlLine;
1✔
2080
            this.bufferSize = o.bufferSize;
1✔
2081
            this.noEcho = o.noEcho;
1✔
2082
            this.noHeaders = o.noHeaders;
1✔
2083
            this.noNoResponders = o.noNoResponders;
1✔
2084
            this.clientSideLimitChecks = o.clientSideLimitChecks;
1✔
2085
            this.supportUTF8Subjects = o.supportUTF8Subjects;
1✔
2086
            this.inboxPrefix = o.inboxPrefix;
1✔
2087
            this.traceConnection = o.traceConnection;
1✔
2088
            this.maxMessagesInOutgoingQueue = o.maxMessagesInOutgoingQueue;
1✔
2089
            this.discardMessagesWhenOutgoingQueueFull = o.discardMessagesWhenOutgoingQueueFull;
1✔
2090

2091
            this.authHandler = o.authHandler;
1✔
2092
            this.reconnectDelayHandler = o.reconnectDelayHandler;
1✔
2093

2094
            this.errorListener = o.errorListener;
1✔
2095
            this.timeTraceLogger = o.timeTraceLogger;
1✔
2096
            this.connectionListener = o.connectionListener;
1✔
2097
            this.readListener = o.readListener;
1✔
2098
            this.statisticsCollector = o.statisticsCollector;
1✔
2099
            this.dataPortType = o.dataPortType;
1✔
2100
            this.trackAdvancedStats = o.trackAdvancedStats;
1✔
2101
            this.executor = o.executor;
1✔
2102
            this.scheduledExecutor = o.scheduledExecutor;
1✔
2103
            this.callbackThreadFactory = o.callbackThreadFactory;
1✔
2104
            this.connectThreadFactory = o.connectThreadFactory;
1✔
2105
            this.httpRequestInterceptors = o.httpRequestInterceptors;
1✔
2106
            this.proxy = o.proxy;
1✔
2107

2108
            this.ignoreDiscoveredServers = o.ignoreDiscoveredServers;
1✔
2109
            this.tlsFirst = o.tlsFirst;
1✔
2110
            this.useTimeoutException = o.useTimeoutException;
1✔
2111
            this.useDispatcherWithExecutor = o.useDispatcherWithExecutor;
1✔
2112
            this.forceFlushOnRequest = o.forceFlushOnRequest;
1✔
2113

2114
            this.serverPool = o.serverPool;
1✔
2115
            this.dispatcherFactory = o.dispatcherFactory;
1✔
2116
            this.enableFastFallback = o.enableFastFallback;
1✔
2117
        }
1✔
2118
    }
2119

2120
    // ----------------------------------------------------------------------------------------------------
2121
    // CONSTRUCTOR
2122
    // ----------------------------------------------------------------------------------------------------
2123
    private Options(Builder b) {
1✔
2124
        this.natsServerUris = Collections.unmodifiableList(b.natsServerUris);
1✔
2125
        this.unprocessedServers = Collections.unmodifiableList(b.unprocessedServers);  // exactly how the user gave them
1✔
2126
        this.noRandomize = b.noRandomize;
1✔
2127
        this.noResolveHostnames = b.noResolveHostnames;
1✔
2128
        this.reportNoResponders = b.reportNoResponders;
1✔
2129
        this.connectionName = b.connectionName;
1✔
2130
        this.verbose = b.verbose;
1✔
2131
        this.pedantic = b.pedantic;
1✔
2132
        this.sslContext = b.sslContext;
1✔
2133
        this.maxReconnect = b.maxReconnect;
1✔
2134
        this.reconnectWait = b.reconnectWait;
1✔
2135
        this.reconnectJitter = b.reconnectJitter;
1✔
2136
        this.reconnectJitterTls = b.reconnectJitterTls;
1✔
2137
        this.connectionTimeout = b.connectionTimeout;
1✔
2138
        this.socketReadTimeoutMillis = b.socketReadTimeoutMillis;
1✔
2139
        this.socketWriteTimeout = b.socketWriteTimeout;
1✔
2140
        this.socketSoLinger = b.socketSoLinger;
1✔
2141
        this.receiveBufferSize = b.receiveBufferSize;
1✔
2142
        this.sendBufferSize = b.sendBufferSize;
1✔
2143
        this.pingInterval = b.pingInterval;
1✔
2144
        this.requestCleanupInterval = b.requestCleanupInterval;
1✔
2145
        this.maxPingsOut = b.maxPingsOut;
1✔
2146
        this.reconnectBufferSize = b.reconnectBufferSize;
1✔
2147
        this.username = b.username;
1✔
2148
        this.password = b.password;
1✔
2149
        this.tokenSupplier = b.tokenSupplier;
1✔
2150
        this.useOldRequestStyle = b.useOldRequestStyle;
1✔
2151
        this.maxControlLine = b.maxControlLine;
1✔
2152
        this.bufferSize = b.bufferSize;
1✔
2153
        this.noEcho = b.noEcho;
1✔
2154
        this.noHeaders = b.noHeaders;
1✔
2155
        this.noNoResponders = b.noNoResponders;
1✔
2156
        this.clientSideLimitChecks = b.clientSideLimitChecks;
1✔
2157
        this.supportUTF8Subjects = b.supportUTF8Subjects;
1✔
2158
        this.inboxPrefix = b.inboxPrefix;
1✔
2159
        this.traceConnection = b.traceConnection;
1✔
2160
        this.maxMessagesInOutgoingQueue = b.maxMessagesInOutgoingQueue;
1✔
2161
        this.discardMessagesWhenOutgoingQueueFull = b.discardMessagesWhenOutgoingQueueFull;
1✔
2162

2163
        this.authHandler = b.authHandler;
1✔
2164
        this.reconnectDelayHandler = b.reconnectDelayHandler;
1✔
2165

2166
        this.errorListener = b.errorListener;
1✔
2167
        this.timeTraceLogger = b.timeTraceLogger;
1✔
2168
        this.connectionListener = b.connectionListener;
1✔
2169
        this.readListener = b.readListener;
1✔
2170
        this.statisticsCollector = b.statisticsCollector;
1✔
2171
        this.dataPortType = b.dataPortType;
1✔
2172
        this.trackAdvancedStats = b.trackAdvancedStats;
1✔
2173
        this.executor = b.executor;
1✔
2174
        this.scheduledExecutor = b.scheduledExecutor;
1✔
2175
        this.callbackThreadFactory = b.callbackThreadFactory;
1✔
2176
        this.connectThreadFactory = b.connectThreadFactory;
1✔
2177
        this.httpRequestInterceptors = b.httpRequestInterceptors;
1✔
2178
        this.proxy = b.proxy;
1✔
2179

2180
        this.ignoreDiscoveredServers = b.ignoreDiscoveredServers;
1✔
2181
        this.tlsFirst = b.tlsFirst;
1✔
2182
        this.useTimeoutException = b.useTimeoutException;
1✔
2183
        this.useDispatcherWithExecutor = b.useDispatcherWithExecutor;
1✔
2184
        this.forceFlushOnRequest = b.forceFlushOnRequest;
1✔
2185

2186
        this.serverPool = b.serverPool;
1✔
2187
        this.dispatcherFactory = b.dispatcherFactory;
1✔
2188
        this.enableFastFallback = b.enableFastFallback;
1✔
2189
    }
1✔
2190

2191
    // ----------------------------------------------------------------------------------------------------
2192
    // GETTERS
2193
    // ----------------------------------------------------------------------------------------------------
2194
    /**
2195
     * @return the executor, see {@link Builder#executor(ExecutorService) executor()} in the builder doc
2196
     */
2197
    public ExecutorService getExecutor() {
2198
        return this.executor == null ? _getInternalExecutor() : this.executor;
1✔
2199
    }
2200

2201
    private ExecutorService _getInternalExecutor() {
2202
        String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
2203
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
1✔
2204
            500L, TimeUnit.MILLISECONDS,
2205
            new SynchronousQueue<>(),
2206
            new DefaultThreadFactory(threadPrefix));
2207
    }
2208

2209
    /**
2210
     * @return the ScheduledExecutorService, see {@link Builder#scheduledExecutor(ScheduledExecutorService) scheduledExecutor()} in the builder doc
2211
     */
2212
    public ScheduledExecutorService getScheduledExecutor() {
2213
        return this.scheduledExecutor == null ? _getInternalScheduledExecutor() : this.scheduledExecutor;
1✔
2214
    }
2215

2216
    private ScheduledExecutorService _getInternalScheduledExecutor() {
2217
        String threadPrefix = nullOrEmpty(this.connectionName) ? DEFAULT_THREAD_NAME_PREFIX : this.connectionName;
1✔
2218
        // the core pool size of 3 is chosen considering where we know the scheduler is used.
2219
        // 1. Ping timer, 2. cleanup timer, 3. SocketDataPortWithWriteTimeout
2220
        // Pull message managers also use a scheduler, but we don't even know if this will be consuming
2221
        ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(3, new DefaultThreadFactory(threadPrefix));
1✔
2222
        stpe.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
1✔
2223
        stpe.setRemoveOnCancelPolicy(true);
1✔
2224
        return stpe;
1✔
2225
    }
2226

2227
    public boolean executorIsInternal() {
2228
        return this.executor == null;
1✔
2229
    }
2230

2231
    public boolean scheduledExecutorIsInternal() {
2232
        return this.scheduledExecutor == null;
1✔
2233
    }
2234

2235
    /**
2236
     * @return the callback executor, see {@link Builder#callbackThreadFactory(ThreadFactory) callbackThreadFactory()} in the builder doc
2237
     */
2238
    public ExecutorService getCallbackExecutor() {
2239
        return this.callbackThreadFactory == null ?
1✔
2240
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.callbackThreadFactory);
1✔
2241
    }
2242

2243
    /**
2244
     * @return the connect executor, see {@link Builder#connectThreadFactory(ThreadFactory) connectThreadFactory()} in the builder doc
2245
     */
2246
    public ExecutorService getConnectExecutor() {
2247
        return this.connectThreadFactory == null ?
1✔
2248
                DEFAULT_SINGLE_THREAD_EXECUTOR.get() : Executors.newSingleThreadExecutor(this.connectThreadFactory);
1✔
2249
    }
2250

2251
    /**
2252
     * @return the list of HttpRequest interceptors.
2253
     */
2254
    public List<java.util.function.Consumer<HttpRequest>> getHttpRequestInterceptors() {
2255
        return null == this.httpRequestInterceptors
1✔
2256
            ? Collections.emptyList()
1✔
2257
            : Collections.unmodifiableList(this.httpRequestInterceptors);
1✔
2258
    }
2259

2260
    /**
2261
     * @return the proxy to used for all sockets.
2262
     */
2263
    public Proxy getProxy() {
2264
        return this.proxy;
1✔
2265
    }
2266

2267
    /**
2268
     * @return the error listener. Will be an instance of ErrorListenerLoggerImpl if not user supplied. See {@link Builder#errorListener(ErrorListener) errorListener()} in the builder doc
2269
     */
2270
    public ErrorListener getErrorListener() {
2271
        return this.errorListener;
1✔
2272
    }
2273

2274
    /**
2275
     * If the user provided a TimeTraceLogger, it's returned here.
2276
     * If the user set traceConnection but did not supply their own, the original time trace logging will occur
2277
     * If the user did not provide a TimeTraceLogger and did not set traceConnection, this will be a no-op implementation.
2278
     * @return the time trace logger
2279
     */
2280
    public TimeTraceLogger getTimeTraceLogger() {
2281
        return this.timeTraceLogger;
1✔
2282
    }
2283

2284
    /**
2285
     * @return the connection listener, or null, see {@link Builder#connectionListener(ConnectionListener) connectionListener()} in the builder doc
2286
     */
2287
    public ConnectionListener getConnectionListener() {
2288
        return this.connectionListener;
1✔
2289
    }
2290

2291
    /**
2292
     * @return the read listener, or null, see {@link Builder#readListener(ReadListener) readListener()} in the builder doc
2293
     */
2294
    public ReadListener getReadListener() {
2295
        return this.readListener;
1✔
2296
    }
2297

2298
    /**
2299
     * @return the statistics collector, or null, see {@link Builder#statisticsCollector(StatisticsCollector) statisticsCollector()} in the builder doc
2300
     */
2301
    public StatisticsCollector getStatisticsCollector() {
2302
        return this.statisticsCollector;
1✔
2303
    }
2304

2305
    /**
2306
     * @return the auth handler, or null, see {@link Builder#authHandler(AuthHandler) authHandler()} in the builder doc
2307
     */
2308
    public AuthHandler getAuthHandler() {
2309
        return this.authHandler;
1✔
2310
    }
2311

2312
    /**
2313
     * @return the reconnection delay handler, or null, see {@link Builder#reconnectDelayHandler(ReconnectDelayHandler) reconnectDelayHandler()} in the builder doc
2314
     */
2315
    public ReconnectDelayHandler getReconnectDelayHandler() {
2316
        return this.reconnectDelayHandler;
1✔
2317
    }
2318

2319
    /**
2320
     * @return the dataport type for connections created by this options object, see {@link Builder#dataPortType(String) dataPortType()} in the builder doc
2321
     */
2322
    public String getDataPortType() {
2323
        return this.dataPortType;
1✔
2324
    }
2325

2326
    /**
2327
     * @return the data port described by these options
2328
     */
2329
    public DataPort buildDataPort() {
2330
        DataPort dp;
2331
        if (dataPortType.equals(DEFAULT_DATA_PORT_TYPE)) {
1✔
2332
            if (socketWriteTimeout == null) {
1✔
2333
                dp = new SocketDataPort();
1✔
2334
            }
2335
            else {
2336
                dp = new SocketDataPortWithWriteTimeout();
1✔
2337
            }
2338
        }
2339
        else {
2340
            dp = (DataPort) Options.createInstanceOf(dataPortType);
1✔
2341
        }
2342
        dp.afterConstruct(this);
1✔
2343
        return dp;
1✔
2344
    }
2345

2346
    /**
2347
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2348
     */
2349
    public List<URI> getServers() {
2350
        List<URI> list = new ArrayList<>();
1✔
2351
        for (NatsUri nuri : natsServerUris) {
1✔
2352
            list.add(nuri.getUri());
1✔
2353
        }
1✔
2354
        return list;
1✔
2355
    }
2356

2357
    /**
2358
     * @return the servers configured in options, see {@link Builder#servers(String[]) servers()} in the builder doc
2359
     */
2360
    public List<NatsUri> getNatsServerUris() {
2361
        return natsServerUris;
1✔
2362
    }
2363

2364
    /**
2365
     * @return the servers as given to the options, since the servers are normalized
2366
     */
2367
    public List<String> getUnprocessedServers() {
2368
        return unprocessedServers;
1✔
2369
    }
2370

2371
    /**
2372
     * @return should we turn off randomization for server connection attempts, see {@link Builder#noRandomize() noRandomize()} in the builder doc
2373
     */
2374
    public boolean isNoRandomize() {
2375
        return noRandomize;
1✔
2376
    }
2377

2378
    /**
2379
     * @return should we resolve hostnames for server connection attempts, see {@link Builder#noResolveHostnames() noResolveHostnames()} in the builder doc
2380
     */
2381
    public boolean isNoResolveHostnames() {
2382
        return noResolveHostnames;
1✔
2383
    }
2384

2385
    /**
2386
     * @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
2387
     */
2388
    public boolean isReportNoResponders() {
2389
        return reportNoResponders;
1✔
2390
    }
2391

2392
    /**
2393
     * @return the connectionName, see {@link Builder#connectionName(String) connectionName()} in the builder doc
2394
     */
2395
    public String getConnectionName() {
2396
        return connectionName;
1✔
2397
    }
2398

2399
    /**
2400
     * @return are we in verbose mode, see {@link Builder#verbose() verbose()} in the builder doc
2401
     */
2402
    public boolean isVerbose() {
2403
        return verbose;
1✔
2404
    }
2405

2406
    /**
2407
     * @return is echo-ing disabled, see {@link Builder#noEcho() noEcho()} in the builder doc
2408
     */
2409
    public boolean isNoEcho() {
2410
        return noEcho;
1✔
2411
    }
2412

2413
    /**
2414
     * @return are headers disabled, see {@link Builder#noHeaders() noHeaders()} in the builder doc
2415
     */
2416
    public boolean isNoHeaders() {
2417
        return noHeaders;
1✔
2418
    }
2419

2420
    /**
2421
     * @return is NoResponders ignored disabled, see {@link Builder#noNoResponders() noNoResponders()} in the builder doc
2422
     */
2423
    public boolean isNoNoResponders() {
2424
        return noNoResponders;
1✔
2425
    }
2426

2427
    /**
2428
     * @return clientSideLimitChecks flag
2429
     */
2430
    public boolean clientSideLimitChecks() {
2431
        return clientSideLimitChecks;
1✔
2432
    }
2433

2434
    /**
2435
     * @return whether utf8 subjects are supported, see {@link Builder#supportUTF8Subjects() supportUTF8Subjects()} in the builder doc.
2436
     */
2437
    public boolean supportUTF8Subjects() {
2438
        return supportUTF8Subjects;
1✔
2439
    }
2440

2441
    /**
2442
     * @return are we using pedantic protocol, see {@link Builder#pedantic() pedantic()} in the builder doc
2443
     */
2444
    public boolean isPedantic() {
2445
        return pedantic;
1✔
2446
    }
2447

2448
    /**
2449
     * @return should we track advanced stats, see {@link Builder#turnOnAdvancedStats() turnOnAdvancedStats()} in the builder doc
2450
     */
2451
    public boolean isTrackAdvancedStats() {
2452
        return trackAdvancedStats;
1✔
2453
    }
2454

2455
    /**
2456
     * If isTraceConnection is true, the user provided a TimeTraceLogger or manually called traceConnection in the builder
2457
     * @return should we trace the connection?
2458
     */
2459
    public boolean isTraceConnection() {
2460
        return traceConnection;
1✔
2461
    }
2462

2463
    /**
2464
     * @return the maximum length of a control line, see {@link Builder#maxControlLine(int) maxControlLine()} in the builder doc
2465
     */
2466
    public int getMaxControlLine() {
2467
        return maxControlLine;
1✔
2468
    }
2469

2470
    /**
2471
     *
2472
     * @return true if there is an sslContext for these Options, otherwise false, see {@link Builder#secure() secure()} in the builder doc
2473
     */
2474
    public boolean isTLSRequired() {
2475
        return sslContext != null;
1✔
2476
    }
2477

2478
    /**
2479
     * @return the sslContext, see {@link Builder#secure() secure()} in the builder doc
2480
     */
2481
    public SSLContext getSslContext() {
2482
        return sslContext;
1✔
2483
    }
2484

2485
    /**
2486
     * @return the maxReconnect attempts to make before failing, see {@link Builder#maxReconnects(int) maxReconnects()} in the builder doc
2487
     */
2488
    public int getMaxReconnect() {
2489
        return maxReconnect;
1✔
2490
    }
2491

2492
    /**
2493
     * @return the reconnectWait, used between reconnect attempts, see {@link Builder#reconnectWait(Duration) reconnectWait()} in the builder doc
2494
     */
2495
    public Duration getReconnectWait() {
2496
        return reconnectWait;
1✔
2497
    }
2498

2499
    /**
2500
     * @return the reconnectJitter, used between reconnect attempts to vary the reconnect wait, see {@link Builder#reconnectJitter(Duration) reconnectJitter()} in the builder doc
2501
     */
2502
    public Duration getReconnectJitter() {
2503
        return reconnectJitter;
1✔
2504
    }
2505

2506
    /**
2507
     * @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
2508
     */
2509
    public Duration getReconnectJitterTls() {
2510
        return reconnectJitterTls;
1✔
2511
    }
2512

2513
    /**
2514
     * @return the connectionTimeout, see {@link Builder#connectionTimeout(Duration) connectionTimeout()} in the builder doc
2515
     */
2516
    public Duration getConnectionTimeout() {
2517
        return connectionTimeout;
1✔
2518
    }
2519

2520
    /**
2521
     * @return the socketReadTimeoutMillis, see {@link Builder#socketReadTimeoutMillis(int) socketReadTimeoutMillis} in the builder doc
2522
     */
2523
    public int getSocketReadTimeoutMillis() {
2524
        return socketReadTimeoutMillis;
1✔
2525
    }
2526

2527
    /**
2528
     * @return the socketWriteTimeout, see {@link Builder#socketWriteTimeout(long) socketWriteTimeout} in the builder doc
2529
     */
2530
    public Duration getSocketWriteTimeout() {
2531
        return socketWriteTimeout;
1✔
2532
    }
2533

2534
    /**
2535
     * @return the socket so linger number of seconds, see {@link Builder#socketSoLinger(int) socketSoLinger()} in the builder doc
2536
     */
2537
    public int getSocketSoLinger() {
2538
        return socketSoLinger;
1✔
2539
    }
2540

2541
    /**
2542
     * @return the number of bytes to set the for the SO_RCVBUF property on the socket  
2543
     */
2544
    public int getReceiveBufferSize() {
2545
        return receiveBufferSize;
1✔
2546
    }
2547

2548
    /**
2549
     * @return the number of bytes to set the for the SO_SNDBUF property on the socket  
2550
     */
2551
    public int getSendBufferSize() {
2552
        return sendBufferSize;
1✔
2553
    }
2554

2555
    /**
2556
     * @return the pingInterval, see {@link Builder#pingInterval(Duration) pingInterval()} in the builder doc
2557
     */
2558
    public Duration getPingInterval() {
2559
        return pingInterval;
1✔
2560
    }
2561

2562
    /**
2563
     * @return the request cleanup interval, see {@link Builder#requestCleanupInterval(Duration) requestCleanupInterval()} in the builder doc
2564
     */
2565
    public Duration getRequestCleanupInterval() {
2566
        return requestCleanupInterval;
1✔
2567
    }
2568

2569
    /**
2570
     * @return the maxPingsOut to limit the number of pings on the wire, see {@link Builder#maxPingsOut(int) maxPingsOut()} in the builder doc
2571
     */
2572
    public int getMaxPingsOut() {
2573
        return maxPingsOut;
1✔
2574
    }
2575

2576
    /**
2577
     * @return the reconnectBufferSize, to limit the amount of data held during
2578
     *         reconnection attempts, see {@link Builder#reconnectBufferSize(long) reconnectBufferSize()} in the builder doc
2579
     */
2580
    public long getReconnectBufferSize() {
2581
        return reconnectBufferSize;
1✔
2582
    }
2583

2584
    /**
2585
     * @return the default size for buffers in the connection code, see {@link Builder#bufferSize(int) bufferSize()} in the builder doc
2586
     */
2587
    public int getBufferSize() {
2588
        return bufferSize;
1✔
2589
    }
2590

2591
    /**
2592
     * @deprecated converts the char array to a string, use getUserNameChars instead for more security
2593
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2594
     */
2595
    @Deprecated
2596
    public String getUsername() {
2597
        return username == null ? null : new String(username);
1✔
2598
    }
2599

2600
    /**
2601
     * @return the username to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2602
     */
2603
    public char[] getUsernameChars() {
2604
        return username;
1✔
2605
    }
2606

2607
    /**
2608
     * @deprecated converts the char array to a string, use getPasswordChars instead for more security
2609
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2610
     */
2611
    @Deprecated
2612
    public String getPassword() {
2613
        return password == null ? null : new String(password);
1✔
2614
    }
2615

2616
    /**
2617
     * @return the password to use for basic authentication, see {@link Builder#userInfo(String, String) userInfo()} in the builder doc
2618
     */
2619
    public char[] getPasswordChars() {
2620
        return password;
1✔
2621
    }
2622

2623
    /**
2624
     * @deprecated converts the char array to a string, use getTokenChars instead for more security
2625
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2626
     */
2627
    @Deprecated
2628
    public String getToken() {
2629
        char[] token = tokenSupplier.get();
1✔
2630
        return token == null ? null : new String(token);
1✔
2631
    }
2632

2633
    /**
2634
     * @return the token to be used for token-based authentication, see {@link Builder#token(String) token()} in the builder doc
2635
     */
2636
    public char[] getTokenChars() {
2637
        return tokenSupplier.get();
1✔
2638
    }
2639

2640
    /**
2641
     * @return the flag to turn on old style requests, see {@link Builder#oldRequestStyle() oldStyleRequest()} in the builder doc
2642
     */
2643
    public boolean isOldRequestStyle() {
2644
        return useOldRequestStyle;
1✔
2645
    }
2646

2647
    /**
2648
     * @return the inbox prefix to use for requests, see {@link Builder#inboxPrefix(String) inboxPrefix()} in the builder doc
2649
     */
2650
    public String getInboxPrefix() {
2651
        return inboxPrefix;
1✔
2652
    }
2653

2654
    /**
2655
     * @return the maximum number of messages in the outgoing queue, see {@link Builder#maxMessagesInOutgoingQueue(int)
2656
     * maxMessagesInOutgoingQueue(int)} in the builder doc
2657
     */
2658
    public int getMaxMessagesInOutgoingQueue() {
2659
        return maxMessagesInOutgoingQueue;
1✔
2660
    }
2661

2662
    /**
2663
     * @return should we discard messages when the outgoing queue is full, see {@link Builder#discardMessagesWhenOutgoingQueueFull()
2664
     * discardMessagesWhenOutgoingQueueFull()} in the builder doc
2665
     */
2666
    public boolean isDiscardMessagesWhenOutgoingQueueFull() {
2667
        return discardMessagesWhenOutgoingQueueFull;
1✔
2668
    }
2669

2670
    /**
2671
     * Get whether to ignore discovered servers
2672
     * @return the flag
2673
     */
2674
    public boolean isIgnoreDiscoveredServers() {
2675
        return ignoreDiscoveredServers;
1✔
2676
    }
2677

2678
    /**
2679
     * Get whether to do tls first
2680
     * @return the flag
2681
     */
2682
    public boolean isTlsFirst() {
2683
        return tlsFirst;
1✔
2684
    }
2685

2686
    /**
2687
     * Get whether to throw {@link java.util.concurrent.TimeoutException} on timeout instead of {@link java.util.concurrent.CancellationException}.
2688
     * @return the flag
2689
     */
2690
    public boolean useTimeoutException() {
2691
        return useTimeoutException;
1✔
2692
    }
2693

2694
    /**
2695
     * Whether the dispatcher should use an executor to async messages to handlers
2696
     * @return the flag
2697
     */
2698
    public boolean useDispatcherWithExecutor() { return useDispatcherWithExecutor; }
1✔
2699

2700
    /**
2701
     * Whether to flush on any user request
2702
     * @return the flag
2703
     */
2704
    public boolean forceFlushOnRequest() {
2705
        return forceFlushOnRequest;
1✔
2706
    }
2707

2708
    /**
2709
     * Get the ServerPool implementation. If null, a default implementation is used.
2710
     * @return the ServerPool implementation
2711
     */
2712
    public ServerPool getServerPool() {
2713
        return serverPool;
1✔
2714
    }
2715

2716
    /**
2717
     * Get the DispatcherFactory implementation. If null, a default implementation is used.
2718
     * @return the DispatcherFactory implementation
2719
     */
2720
    public DispatcherFactory getDispatcherFactory() {
2721
        return dispatcherFactory;
1✔
2722
    }
2723

2724
    /**
2725
     * Whether Fast fallback algorithm is enabled for socket connect
2726
     * @return the flag
2727
     */
2728
    public boolean isEnableFastFallback() {
2729
        return enableFastFallback;
1✔
2730
    }
2731

2732
    public URI createURIForServer(String serverURI) throws URISyntaxException {
2733
        return new NatsUri(serverURI).getUri();
1✔
2734
    }
2735

2736
    /**
2737
     * Create the options string sent with the connect message.
2738
     * If includeAuth is true the auth information is included:
2739
     * If the server URIs have auth info it is used. Otherwise, the userInfo is used.
2740
     * @param serverURI the current server uri
2741
     * @param includeAuth tells the options to build a connection string that includes auth information
2742
     * @param nonce if the client is supposed to sign the nonce for authentication
2743
     * @return the options String, basically JSON
2744
     */
2745
    public CharBuffer buildProtocolConnectOptionsString(String serverURI, boolean includeAuth, byte[] nonce) {
2746
        CharBuffer connectString = CharBuffer.allocate(this.maxControlLine);
1✔
2747
        connectString.append("{");
1✔
2748

2749
        appendOption(connectString, Options.OPTION_LANG, Nats.CLIENT_LANGUAGE, true, false);
1✔
2750
        appendOption(connectString, Options.OPTION_VERSION, Nats.CLIENT_VERSION, true, true);
1✔
2751

2752
        if (this.connectionName != null) {
1✔
2753
            appendOption(connectString, Options.OPTION_NAME, this.connectionName, true, true);
1✔
2754
        }
2755

2756
        appendOption(connectString, Options.OPTION_PROTOCOL, "1", false, true);
1✔
2757

2758
        appendOption(connectString, Options.OPTION_VERBOSE, String.valueOf(this.isVerbose()), false, true);
1✔
2759
        appendOption(connectString, Options.OPTION_PEDANTIC, String.valueOf(this.isPedantic()), false, true);
1✔
2760
        appendOption(connectString, Options.OPTION_TLS_REQUIRED, String.valueOf(this.isTLSRequired()), false, true);
1✔
2761
        appendOption(connectString, Options.OPTION_ECHO, String.valueOf(!this.isNoEcho()), false, true);
1✔
2762
        appendOption(connectString, Options.OPTION_HEADERS, String.valueOf(!this.isNoHeaders()), false, true);
1✔
2763
        appendOption(connectString, Options.OPTION_NORESPONDERS, String.valueOf(!this.isNoNoResponders()), false, true);
1✔
2764

2765
        if (includeAuth) {
1✔
2766
            if (nonce != null && this.getAuthHandler() != null) {
1✔
2767
                char[] nkey = this.getAuthHandler().getID();
1✔
2768
                byte[] sig = this.getAuthHandler().sign(nonce);
1✔
2769
                char[] jwt = this.getAuthHandler().getJWT();
1✔
2770

2771
                if (sig == null) {
1✔
2772
                    sig = new byte[0];
1✔
2773
                }
2774

2775
                if (jwt == null) {
1✔
2776
                    jwt = new char[0];
1✔
2777
                }
2778

2779
                if (nkey == null) {
1✔
2780
                    nkey = new char[0];
1✔
2781
                }
2782

2783
                String encodedSig = base64UrlEncodeToString(sig);
1✔
2784

2785
                appendOption(connectString, Options.OPTION_NKEY, nkey, true);
1✔
2786
                appendOption(connectString, Options.OPTION_SIG, encodedSig, true, true);
1✔
2787
                appendOption(connectString, Options.OPTION_JWT, jwt, true);
1✔
2788
            }
2789

2790
            String uriUser = null;
1✔
2791
            String uriPass = null;
1✔
2792
            String uriToken = null;
1✔
2793

2794
            // Values from URI override options
2795
            try {
2796
                URI uri = this.createURIForServer(serverURI);
1✔
2797
                String userInfo = uri.getRawUserInfo();
1✔
2798
                if (userInfo != null) {
1✔
2799
                    int at = userInfo.indexOf(":");
1✔
2800
                    if (at == -1) {
1✔
2801
                        uriToken = uriDecode(userInfo);
1✔
2802
                    }
2803
                    else {
2804
                        uriUser = uriDecode(userInfo.substring(0, at));
1✔
2805
                        uriPass = uriDecode(userInfo.substring(at + 1));
1✔
2806
                    }
2807
                }
2808
            }
2809
            catch (URISyntaxException e) {
×
2810
                // the createURIForServer call is the one that potentially throws this
2811
                // uriUser, uriPass and uriToken will already be null
2812
            }
1✔
2813

2814
            if (uriUser != null) {
1✔
2815
                appendOption(connectString, Options.OPTION_USER, jsonEncode(uriUser), true, true);
1✔
2816
            }
2817
            else if (this.username != null) {
1✔
2818
                appendOption(connectString, Options.OPTION_USER, jsonEncode(this.username), true, true);
1✔
2819
            }
2820

2821
            if (uriPass != null) {
1✔
2822
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(uriPass), true, true);
1✔
2823
            }
2824
            else if (this.password != null) {
1✔
2825
                appendOption(connectString, Options.OPTION_PASSWORD, jsonEncode(this.password), true, true);
1✔
2826
            }
2827

2828
            if (uriToken != null) {
1✔
2829
                appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
1✔
2830
            }
2831
            else {
2832
                char[] token = this.tokenSupplier.get();
1✔
2833
                if (token != null) {
1✔
2834
                    appendOption(connectString, Options.OPTION_AUTH_TOKEN, token, true);
1✔
2835
                }
2836
            }
2837
        }
2838

2839
        connectString.append("}");
1✔
2840
        connectString.flip();
1✔
2841
        return connectString;
1✔
2842
    }
2843

2844
    // ----------------------------------------------------------------------------------------------------
2845
    // HELPER FUNCTIONS
2846
    // ----------------------------------------------------------------------------------------------------
2847
    private static void appendOption(CharBuffer builder, String key, String value, boolean quotes, boolean comma) {
2848
        _appendStart(builder, key, quotes, comma);
1✔
2849
        builder.append(value);
1✔
2850
        _appendOptionEnd(builder, quotes);
1✔
2851
    }
1✔
2852

2853
    @SuppressWarnings("SameParameterValue")
2854
    private static void appendOption(CharBuffer builder, String key, char[] value, boolean comma) {
2855
        _appendStart(builder, key, true, comma);
1✔
2856
        builder.put(value);
1✔
2857
        _appendOptionEnd(builder, true);
1✔
2858
    }
1✔
2859

2860
    private static void _appendStart(CharBuffer builder, String key, boolean quotes, boolean comma) {
2861
        if (comma) {
1✔
2862
            builder.append(',');
1✔
2863
        }
2864
        builder.append('"');
1✔
2865
        builder.append(key);
1✔
2866
        builder.append('"');
1✔
2867
        builder.append(':');
1✔
2868
        _appendOptionEnd(builder, quotes);
1✔
2869
    }
1✔
2870

2871
    private static void _appendOptionEnd(CharBuffer builder, boolean quotes) {
2872
        if (quotes) {
1✔
2873
            builder.append('"');
1✔
2874
        }
2875
    }
1✔
2876

2877
    private static String getPropertyValue(Properties props, String key) {
2878
        String value = emptyAsNull(props.getProperty(key));
1✔
2879
        if (value != null) {
1✔
2880
            return value;
1✔
2881
        }
2882
        if (key.startsWith(PFX)) { // if the key starts with the PFX, check the non PFX
1✔
2883
            return emptyAsNull(props.getProperty(key.substring(PFX_LEN)));
1✔
2884
        }
2885
        // otherwise check with the PFX
2886
        value = emptyAsNull(props.getProperty(PFX + key));
1✔
2887
        if (value == null && key.contains("_")) {
1✔
2888
            // addressing where underscore was used in a key value instead of dot
2889
            return getPropertyValue(props, key.replace("_", "."));
1✔
2890
        }
2891
        return value;
1✔
2892
    }
2893

2894
    private static void stringProperty(Properties props, String key, java.util.function.Consumer<String> consumer) {
2895
        String value = getPropertyValue(props, key);
1✔
2896
        if (value != null) {
1✔
2897
            consumer.accept(value);
1✔
2898
        }
2899
    }
1✔
2900

2901
    private static void charArrayProperty(Properties props, String key, java.util.function.Consumer<char[]> consumer) {
2902
        String value = getPropertyValue(props, key);
1✔
2903
        if (value != null) {
1✔
2904
            consumer.accept(value.toCharArray());
1✔
2905
        }
2906
    }
1✔
2907

2908
    private static void booleanProperty(Properties props, String key, java.util.function.Consumer<Boolean> consumer) {
2909
        String value = getPropertyValue(props, key);
1✔
2910
        if (value != null) {
1✔
2911
            consumer.accept(Boolean.parseBoolean(value));
1✔
2912
        }
2913
    }
1✔
2914

2915
    private static void intProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2916
        String value = getPropertyValue(props, key);
1✔
2917
        if (value == null) {
1✔
2918
            consumer.accept(defaultValue);
1✔
2919
        }
2920
        else {
2921
            consumer.accept(Integer.parseInt(value));
1✔
2922
        }
2923
    }
1✔
2924

2925
    private static void intGtEqZeroProperty(Properties props, String key, int defaultValue, java.util.function.Consumer<Integer> consumer) {
2926
        String value = getPropertyValue(props, key);
1✔
2927
        if (value == null) {
1✔
2928
            consumer.accept(defaultValue);
1✔
2929
        }
2930
        else {
2931
            int i = Integer.parseInt(value);
1✔
2932
            if (i < 0) {
1✔
2933
                consumer.accept(defaultValue);
1✔
2934
            }
2935
            else {
2936
                consumer.accept(i);
1✔
2937
            }
2938
        }
2939
    }
1✔
2940

2941
    private static void longProperty(Properties props, String key, long defaultValue, java.util.function.Consumer<Long> consumer) {
2942
        String value = getPropertyValue(props, key);
1✔
2943
        if (value == null) {
1✔
2944
            consumer.accept(defaultValue);
1✔
2945
        }
2946
        else {
2947
            consumer.accept(Long.parseLong(value));
1✔
2948
        }
2949
    }
1✔
2950

2951
    private static void durationProperty(Properties props, String key, Duration defaultValue, java.util.function.Consumer<Duration> consumer) {
2952
        String value = getPropertyValue(props, key);
1✔
2953
        if (value == null) {
1✔
2954
            consumer.accept(defaultValue);
1✔
2955
        }
2956
        else {
2957
            try {
2958
                Duration d = Duration.parse(value);
1✔
2959
                if (d.toNanos() < 0) {
1✔
2960
                    consumer.accept(defaultValue);
×
2961
                }
2962
                else {
2963
                    consumer.accept(d);
1✔
2964
                }
2965
            }
2966
            catch (DateTimeParseException pe) {
1✔
2967
                int ms = Integer.parseInt(value);
1✔
2968
                if (ms < 0) {
1✔
2969
                    consumer.accept(defaultValue);
1✔
2970
                }
2971
                else {
2972
                    consumer.accept(Duration.ofMillis(ms));
1✔
2973
                }
2974
            }
1✔
2975
        }
2976
    }
1✔
2977

2978
    private static void classnameProperty(Properties props, String key, java.util.function.Consumer<Object> consumer) {
2979
        stringProperty(props, key, className -> consumer.accept(createInstanceOf(className)));
1✔
2980
    }
1✔
2981

2982
    private static Object createInstanceOf(String className) {
2983
        try {
2984
            Class<?> clazz = Class.forName(className);
1✔
2985
            Constructor<?> constructor = clazz.getConstructor();
1✔
2986
            return constructor.newInstance();
1✔
2987
        } catch (Exception e) {
1✔
2988
            throw new IllegalArgumentException(e);
1✔
2989
        }
2990
    }
2991
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc