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

nats-io / java-nats-server-runner / #23

06 Jul 2025 08:22PM UTC coverage: 89.52% (+0.08%) from 89.445%
#23

push

github

scottf
Fix double port in config

504 of 563 relevant lines covered (89.52%)

0.9 hits per line

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

98.04
/src/main/java/io/nats/NatsServerRunner.java
1
// Copyright 2020-2023 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;
15

16
import java.io.*;
17
import java.net.ConnectException;
18
import java.net.InetSocketAddress;
19
import java.net.SocketAddress;
20
import java.nio.channels.SocketChannel;
21
import java.nio.file.Files;
22
import java.nio.file.Path;
23
import java.nio.file.Paths;
24
import java.util.*;
25
import java.util.function.Supplier;
26
import java.util.logging.Level;
27
import java.util.logging.Logger;
28
import java.util.regex.Matcher;
29
import java.util.regex.Pattern;
30

31
import static io.nats.NatsRunnerUtils.*;
32

33
/**
34
 * Server Runner
35
 */
36
public class NatsServerRunner implements AutoCloseable {
37
    public static final String ERROR_NOTE_PART_1 = "Make sure that the nats-server is installed and in your PATH.";
38
    public static final String ERROR_NOTE_PART_2 = "See https://github.com/nats-io/nats-server for information on installation";
39
    public static long DEFAULT_PROCESS_CHECK_WAIT = 100;
1✔
40
    public static int DEFAULT_PROCESS_CHECK_TRIES = 10;
1✔
41
    public static long DEFAULT_RUN_CHECK_WAIT = 100;
1✔
42
    public static int DEFAULT_RUN_CHECK_TRIES = 3;
1✔
43

44
    private final String _executablePath;
45
    private final Output _displayOut;
46
    private final Map<String, Integer> _ports;
47
    private final File _configFile;
48
    private final String _cmdLine;
49
    private Process process;
50

51
    /**
52
     * Get a new Builder
53
     * @return the builder
54
     */
55
    public static Builder builder() {
56
        return new Builder();
1✔
57
    }
58

59
    /**
60
     * Construct and start the Nats Server runner with all defaults:
61
     * <ul>
62
     * <li>use an automatically allocated port</li>
63
     * <li>no debug flag</li>
64
     * <li>jetstream not enabled</li>
65
     * <li>no custom config file</li>
66
     * <li>no config inserts</li>
67
     * <li>no custom args</li>
68
     * </ul>
69
     * @throws IOException thrown when the server cannot start
70
     */
71
    public NatsServerRunner() throws IOException {
72
        this(builder());
1✔
73
    }
1✔
74

75
    /**
76
     * Construct and start the Nats Server runner with defaults:
77
     * <ul>
78
     * <li>use an automatically allocated port</li>
79
     * <li>jetstream not enabled</li>
80
     * <li>no custom config file</li>
81
     * <li>no config inserts</li>
82
     * <li>no custom args</li>
83
     * </ul>
84
     * and this option:
85
     * @param debug whether to start the server with the -DV flags
86
     * @throws IOException thrown when the server cannot start
87
     */
88
    public NatsServerRunner(boolean debug) throws IOException {
89
        this(builder().debug(debug));
1✔
90
    }
1✔
91

92
    /**
93
     * Construct and start the Nats Server runner with defaults:
94
     * Consider using {@link Builder}
95
     * <ul>
96
     * <li>use an automatically allocated port</li>
97
     * <li>no custom config file</li>
98
     * <li>no config inserts</li>
99
     * <li>no custom args</li>
100
     * </ul>
101
     * and these options:
102
     * @param debug whether to start the server with the -DV flags
103
     * @param jetstream whether to enable JetStream
104
     * @throws IOException thrown when the server cannot start
105
     */
106
    public NatsServerRunner(boolean debug, boolean jetstream) throws IOException {
107
        this(builder().debug(debug).jetstream(jetstream));
1✔
108
    }
1✔
109

110
    /**
111
     * Construct and start the Nats Server runner with defaults:
112
     * <ul>
113
     * <li>jetstream not enabled</li>
114
     * <li>no custom config file</li>
115
     * <li>no config inserts</li>
116
     * <li>no custom args</li>
117
     * </ul>
118
     * and these options:
119
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
120
     * @param debug whether to start the server with the -DV flags
121
     * @throws IOException thrown when the server cannot start
122
     */
123
    public NatsServerRunner(int port, boolean debug) throws IOException {
124
        this(builder().port(port).debug(debug));
1✔
125
    }
1✔
126

127
    /**
128
     * Construct and start the Nats Server runner with defaults:
129
     * <ul>
130
     * <li>no custom config file</li>
131
     * <li>no config inserts</li>
132
     * <li>no custom args</li>
133
     * </ul>
134
     * and these options:
135
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
136
     * @param debug whether to start the server with the -DV flags
137
     * @param jetstream whether to enable JetStream
138
     * @throws IOException thrown when the server cannot start
139
     */
140
    public NatsServerRunner(int port, boolean debug, boolean jetstream) throws IOException {
141
        this(builder().port(port).debug(debug).jetstream(jetstream));
1✔
142
    }
1✔
143

144
    /**
145
     * Construct and start the Nats Server runner with defaults:
146
     * Consider using {@link Builder}
147
     * <ul>
148
     * <li>use an automatically allocated port</li>
149
     * <li>jetstream not enabled</li>
150
     * <li>no config inserts</li>
151
     * <li>no custom args</li>
152
     * </ul>
153
     * and these options:
154
     * @param debug whether to start the server with the -DV flags
155
     * @param configFilePath path to a custom config file
156
     * @throws IOException thrown when the server cannot start
157
     */
158
    public NatsServerRunner(String configFilePath, boolean debug) throws IOException {
159
        this(builder().debug(debug).configFilePath(configFilePath));
1✔
160
    }
1✔
161

162
    /**
163
     * Construct and start the Nats Server runner with defaults:
164
     * Consider using {@link Builder}
165
     * <ul>
166
     * <li>use an automatically allocated port</li>
167
     * <li>jetstream not enabled</li>
168
     * <li>no custom config file</li>
169
     * </ul>
170
     * and these options:
171
     * @param debug whether to start the server with the -DV flags
172
     * @param jetstream whether to enable JetStream
173
     * @param configFilePath path to a custom config file
174
     * @throws IOException thrown when the server cannot start
175
     */
176
    public NatsServerRunner(String configFilePath, boolean debug, boolean jetstream) throws IOException {
177
        this(builder().debug(debug).jetstream(jetstream).configFilePath(configFilePath));
1✔
178
    }
1✔
179

180
    /**
181
     * Construct and start the Nats Server runner with defaults:
182
     * Consider using {@link Builder}
183
     * <ul>
184
     * <li>jetstream not enabled</li>
185
     * <li>no custom args</li>
186
     * </ul>
187
     * and these options:
188
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
189
     * @param debug whether to start the server with the -DV flags
190
     * @param configFilePath path to a custom config file
191
     * @param configInserts an array of custom lines to add to the config file
192
     * @throws IOException thrown when the server cannot start
193
     */
194
    public NatsServerRunner(String configFilePath, String[] configInserts, int port, boolean debug) throws IOException {
195
        this(builder().port(port).debug(debug).configFilePath(configFilePath).configInserts(configInserts));
1✔
196
    }
1✔
197

198
    /**
199
     * Construct and start the Nats Server runner with defaults:
200
     * Consider using {@link Builder}
201
     * <ul>
202
     * <li>jetstream not enabled</li>
203
     * <li>no config inserts</li>
204
     * <li>no custom args</li>
205
     * </ul>
206
     * and these options:
207
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
208
     * @param debug whether to start the server with the -DV flags
209
     * @param configFilePath path to a custom config file
210
     * @throws IOException thrown when the server cannot start
211
     */
212
    public NatsServerRunner(String configFilePath, int port, boolean debug) throws IOException {
213
        this(builder().port(port).debug(debug).configFilePath(configFilePath));
1✔
214
    }
1✔
215

216
    /**
217
     * Construct and start the Nats Server runner with defaults:
218
     * Consider using {@link Builder}
219
     * <ul>
220
     * <li>use an automatically allocated port</li>
221
     * <li>no debug flag</li>
222
     * <li>jetstream not enabled</li>
223
     * <li>no custom config file</li>
224
     * <li>no config inserts</li>
225
     * </ul>
226
     * and these options:
227
     * @param customArgs any custom args to add to the command line
228
     * @throws IOException thrown when the server cannot start
229
     */
230
    public NatsServerRunner(String[] customArgs) throws IOException {
231
        this(builder().customArgs(customArgs));
1✔
232
    }
1✔
233

234
    /**
235
     * Construct and start the Nats Server runner with defaults:
236
     * Consider using {@link Builder}
237
     * <ul>
238
     * <li>use an automatically allocated port</li>
239
     * <li>jetstream not enabled</li>
240
     * <li>no custom config file</li>
241
     * <li>no config inserts</li>
242
     * </ul>
243
     * and these options:
244
     * @param debug whether to start the server with the -DV flags
245
     * @param customArgs any custom args to add to the command line
246
     * @throws IOException thrown when the server cannot start
247
     */
248
    public NatsServerRunner(String[] customArgs, boolean debug) throws IOException {
249
        this(builder().debug(debug).customArgs(customArgs));
1✔
250
    }
1✔
251

252
    /**
253
     * Construct and start the Nats Server runner with defaults:
254
     * Consider using {@link Builder}
255
     * <ul>
256
     * <li>use an automatically allocated port</li>
257
     * <li>no custom config file</li>
258
     * <li>no config inserts</li>
259
     * </ul>
260
     * and these options:
261
     * @param customArgs any custom args to add to the command line
262
     * @param debug whether to start the server with the -DV flags
263
     * @param jetstream whether to enable JetStream
264
     * @throws IOException thrown when the server cannot start
265
     */
266
    public NatsServerRunner(String[] customArgs, boolean debug, boolean jetstream) throws IOException {
267
        this(builder().debug(debug).jetstream(jetstream).customArgs(customArgs));
1✔
268
    }
1✔
269

270
    /**
271
     * Construct and start the Nats Server runner with defaults:
272
     * Consider using {@link Builder}
273
     * <ul>
274
     * <li>jetstream not enabled</li>
275
     * <li>no custom config file</li>
276
     * <li>no config inserts</li>
277
     * </ul>
278
     * and these options:
279
     * @param customArgs any custom args to add to the command line
280
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
281
     * @param debug whether to start the server with the -DV flags
282
     * @throws IOException thrown when the server cannot start
283
     */
284
    public NatsServerRunner(String[] customArgs, int port, boolean debug) throws IOException {
285
        this(builder().port(port).debug(debug).customArgs(customArgs));
1✔
286
    }
1✔
287

288
    /**
289
     * Construct and start the Nats Server runner with options
290
     * Consider using {@link Builder}
291
     * @param port the port to start on or &lt;=0 to use an automatically allocated port
292
     * @param debug whether to start the server with the -DV flags
293
     * @param jetstream whether to enable JetStream
294
     * @param configFilePath path to a custom config file
295
     * @param configInserts an array of custom lines to add to the config file
296
     * @param customArgs any custom args to add to the command line
297
     * @throws IOException thrown when the server cannot start
298
     */
299
    public NatsServerRunner(int port, boolean debug, boolean jetstream, String configFilePath, String[] configInserts, String[] customArgs) throws IOException {
300
        this(builder().port(port).debug(debug).jetstream(jetstream).configFilePath(configFilePath).configInserts(configInserts).customArgs(customArgs));
1✔
301
    }
1✔
302

303
    public NatsServerRunner(NatsServerRunnerOptions natsServerRunnerOptions) throws Exception {
304
        this(builder().runnerOptions(natsServerRunnerOptions));
1✔
305
    }
1✔
306

307
    // ----------------------------------------------------------------------------------------------------
308
    // ACTUAL CONSTRUCTION
309
    // ----------------------------------------------------------------------------------------------------
310

311
    protected NatsServerRunner(Builder b) throws IOException {
1✔
312
        _executablePath = b.executablePath == null ? getResolvedServerPath() : b.executablePath.toString();
1✔
313
        _ports = b.ports;
1✔
314
        Integer tempPort = _ports.get(CONFIG_PORT_KEY);
1✔
315
        if (tempPort == null) {
1✔
316
            _ports.put(CONFIG_PORT_KEY, -1);
1✔
317
            tempPort = -1;
1✔
318
        }
319
        if (tempPort == -1) {
1✔
320
            tempPort = nextPort();
1✔
321
        }
322
        _ports.put(USER_PORT_KEY, tempPort);
1✔
323
        int userPort = tempPort;
1✔
324
        _ports.put(NATS_PORT_KEY, -1);
1✔
325
        _ports.put(NON_NATS_PORT_KEY, -1);
1✔
326

327
        if (b.output == null) {
1✔
328
            _displayOut = DefaultOutputSupplier.get();
1✔
329
            _displayOut.setLevel(DefaultOutputLevel);
1✔
330
        }
331
        else {
332
            _displayOut = b.output;
1✔
333
            if (b.outputLevel != null) {
1✔
334
                _displayOut.setLevel(b.outputLevel);
×
335
            }
336
        }
337

338
        long procCheckWait = b.processCheckWait == null ? DEFAULT_PROCESS_CHECK_WAIT : b.processCheckWait;
1✔
339
        int procCheckTries = b.processCheckTries == null ? DEFAULT_PROCESS_CHECK_TRIES : b.processCheckTries;
1✔
340
        long connCheckWait = b.connectCheckWait == null ? DEFAULT_RUN_CHECK_WAIT : b.connectCheckWait;
1✔
341
        int connCheckTries = b.connectCheckTries == null ? DEFAULT_RUN_CHECK_TRIES : b.connectCheckTries;
1✔
342

343
        List<String> cmd = new ArrayList<>();
1✔
344
        cmd.add(_executablePath);
1✔
345

346
        try {
347
            _configFile = File.createTempFile(CONF_FILE_PREFIX, CONF_FILE_EXT);
1✔
348
            BufferedWriter writer = new BufferedWriter(new FileWriter(_configFile));
1✔
349
            boolean portAlreadyDone;
350
            if (b.configFilePath == null) {
1✔
351
                _ports.put(NATS_PORT_KEY, userPort);
1✔
352
                writePortLine(writer, userPort);
1✔
353
                portAlreadyDone = true;
1✔
354
            }
355
            else {
356
                processSuppliedConfigFile(writer, b.configFilePath);
1✔
357
                portAlreadyDone = _ports.get(NATS_PORT_KEY) != -1;
1✔
358
            }
359

360
            if (b.configInserts != null) {
1✔
361
                for (String s : b.configInserts) {
1✔
362
                    if (portAlreadyDone && s.startsWith("port:")) {
1✔
363
                        continue;
1✔
364
                    }
365
                    writeLine(writer, s);
1✔
366
                }
1✔
367
            }
368

369
            writer.flush();
1✔
370
            writer.close();
1✔
371

372
            cmd.add(CONFIG_FILE_OPTION_NAME);
1✔
373
            cmd.add(_configFile.getAbsolutePath());
1✔
374
        }
375
        catch (IOException ioe) {
1✔
376
            _displayOut.error("%%% Error creating config file: " + ioe);
1✔
377
            throw ioe;
1✔
378
        }
1✔
379

380
        // Rewrite the port to a new one, so we don't reuse the same one over and over
381

382
        if (b.jetstream) {
1✔
383
            cmd.add(JETSTREAM_OPTION);
1✔
384
        }
385

386
        if (b.customArgs != null) {
1✔
387
            cmd.addAll(b.customArgs);
1✔
388
        }
389

390
        if (b.debugLevel != null) {
1✔
391
            cmd.add(b.debugLevel.getCmdOption());
1✔
392
        }
393

394
        _cmdLine = String.join(" ", cmd);
1✔
395

396
        NatsOutputLogger nol = null;
1✔
397
        try {
398
            ProcessBuilder pb = new ProcessBuilder(cmd);
1✔
399
            pb.redirectErrorStream(true);
1✔
400
            pb.redirectError(ProcessBuilder.Redirect.PIPE);
1✔
401
            pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
1✔
402
            _displayOut.info("%%% Starting [" + _cmdLine + "] with redirected IO");
1✔
403

404
            process = pb.start();
1✔
405
            nol = NatsOutputLogger.logOutput(_displayOut, process, DEFAULT_NATS_SERVER);
1✔
406

407
            int tries = procCheckTries;
1✔
408
            do {
409
                sleep(procCheckWait);
1✔
410
            }
411
            while (!process.isAlive() && --tries > 0);
1✔
412

413
            SocketAddress addr = new InetSocketAddress("localhost", _ports.get(NATS_PORT_KEY));
1✔
414
            SocketChannel socketChannel = SocketChannel.open();
1✔
415
            socketChannel.configureBlocking(true);
1✔
416
            if (connCheckTries > 0) {
1✔
417
                boolean checking = true;
1✔
418
                tries = connCheckTries;
1✔
419
                do {
420
                    try {
421
                        socketChannel.connect(addr);
1✔
422
                        checking = false;
1✔
423
                    }
424
                    catch (ConnectException e) {
1✔
425
                        if (--tries == 0) {
1✔
426
                            throw e;
×
427
                        }
428
                        sleep(connCheckWait);
1✔
429
                    } finally {
430
                        socketChannel.close();
1✔
431
                    }
432
                } while (checking);
1✔
433
            }
434

435
            _displayOut.info("%%% Started [" + _cmdLine + "]");
1✔
436
            nol.endStartupPhase();
1✔
437
        }
438
        catch (IOException ex) {
1✔
439
            _displayOut.error("%%% Failed to run [" + _cmdLine + "]");
1✔
440
            String exMsg = ex.getMessage();
1✔
441
            if (exMsg != null) {
1✔
442
                _displayOut.error("    " + ex.getMessage());
×
443
            }
444
            _displayOut.error("%%% " + ERROR_NOTE_PART_1);
1✔
445
            _displayOut.error("%%% " + ERROR_NOTE_PART_2);
1✔
446
            StringBuilder exMessage = new StringBuilder("Failed to run [").append(_cmdLine).append("]");
1✔
447
            if (b.fullErrorReportOnStartup) {
1✔
448
                if (nol != null) {
1✔
449
                    for (String line : nol.getStartupLines()) {
1✔
450
                        exMessage.append(System.lineSeparator()).append(line);
1✔
451
                    }
1✔
452
                }
453
                if (_cmdLine.contains(CONFIG_FILE_OPTION_NAME)) {
1✔
454
                    String configPath = _configFile.getAbsolutePath();
1✔
455
                    String configSep = getConfigSep(configPath);
1✔
456
                    exMessage.append(System.lineSeparator()).append(configSep);
1✔
457
                    exMessage.append(System.lineSeparator()).append(configPath);
1✔
458
                    exMessage.append(System.lineSeparator()).append(configSep);
1✔
459
                    try {
460
                        List<String> lines = Files.readAllLines(_configFile.toPath());
1✔
461
                        for (String line : lines) {
1✔
462
                            exMessage.append(System.lineSeparator()).append(line);
1✔
463
                        }
1✔
464
                    }
465
                    catch (Exception ignore) {
×
466
                    }
1✔
467
                    exMessage.append(System.lineSeparator()).append(configSep);
1✔
468
                }
469
            }
470
            throw new IllegalStateException(exMessage.toString());
1✔
471
        }
1✔
472
    }
1✔
473

474
    private String getConfigSep(String configPath) {
475
        StringBuilder sep = new StringBuilder("------------------------------");
1✔
476
        int len = configPath.length();
1✔
477
        while (sep.length() < len) {
1✔
478
            sep.append(sep);
1✔
479
        }
480
        return sep.substring(0, len);
1✔
481
    }
482

483
    // ----------------------------------------------------------------------------------------------------
484
    // HELPERS
485
    // ----------------------------------------------------------------------------------------------------
486
    private void processSuppliedConfigFile(BufferedWriter writer, Path configFilePath) throws IOException {
487
        Matcher constructionPortMatcher = Pattern.compile(PORT_REGEX).matcher("");
1✔
488
        Matcher mappedPortMatcher = Pattern.compile(PORT_MAPPED_REGEX).matcher("");
1✔
489

490
        BufferedReader reader = new BufferedReader(new FileReader(configFilePath.toFile()));
1✔
491

492
        boolean userTaken = false;
1✔
493
        int userPort = _ports.get(USER_PORT_KEY); // already ensured so it's not -1
1✔
494
        int natsPort = -1;
1✔
495
        String line = reader.readLine();
1✔
496
        int level = 0;
1✔
497
        while (line != null) {
1✔
498
            String trim = line.trim();
1✔
499
            if (trim.endsWith("{")) {
1✔
500
                level++;
1✔
501
            }
502
            else if (trim.startsWith("}")) {
1✔
503
                level--;
1✔
504
            }
505
            // replacing it here allows us to not care if the port is at the top level
506
            // or for instance inside a websocket block
507
            constructionPortMatcher.reset(line);
1✔
508
            if (constructionPortMatcher.find()) {
1✔
509
                if (userTaken) {
1✔
510
                    throw new IOException("Improper configuration, cannot assign port multiple times.");
1✔
511
                }
512
                userTaken = true;
1✔
513
                if (level == 0) {
1✔
514
                    natsPort = userPort;
1✔
515
                }
516
                else {
517
                    _ports.put(NON_NATS_PORT_KEY, userPort);
1✔
518
                }
519
                writeLine(writer, line.replace(constructionPortMatcher.group(1), Integer.toString(userPort)));
1✔
520
            }
521
            else {
522
                mappedPortMatcher.reset(line);
1✔
523
                if (mappedPortMatcher.find()) {
1✔
524
                    int start = line.indexOf("<");
1✔
525
                    int end = line.indexOf(">");
1✔
526
                    String key = line.substring(start + 1, end);
1✔
527
                    Integer mapped = _ports.get(key);
1✔
528
                    if (mapped == null) {
1✔
529
                        mapped = nextPort();
×
530
                        _ports.put(key, mapped);
×
531
                    }
532
                    writeLine(writer, line.replace("<" + key + ">", mapped.toString()));
1✔
533
                    if (level == 0) {
1✔
534
                        natsPort = mapped;
1✔
535
                    }
536
                    else {
537
                        _ports.put(NON_NATS_PORT_KEY, mapped);
1✔
538
                    }
539
                }
1✔
540
                else {
541
                    writeLine(writer, line);
1✔
542
                }
543
            }
544

545
            line = reader.readLine();
1✔
546
        }
1✔
547

548
        reader.close();
1✔
549

550
        if (natsPort == -1) {
1✔
551
            if (userTaken) {
1✔
552
                _ports.put(NATS_PORT_KEY, 4222);
1✔
553
            }
554
            else {
555
                _ports.put(NATS_PORT_KEY, userPort);
1✔
556
                writePortLine(writer, userPort);
1✔
557
            }
558
        }
559
        else {
560
            _ports.put(NATS_PORT_KEY, natsPort);
1✔
561
        }
562
    }
1✔
563

564
    private void writePortLine(BufferedWriter writer, int port) throws IOException {
565
        writeLine(writer, PORT_PROPERTY + port);
1✔
566
    }
1✔
567

568
    private void writeLine(BufferedWriter writer, String line) throws IOException {
569
        writer.write(line);
1✔
570
        writer.write("\n");
1✔
571
    }
1✔
572

573
    private void sleep(long sleep) {
574
        try {
575
            Thread.sleep(sleep);
1✔
576
        }
577
        catch (Exception ignore) {}
1✔
578
    }
1✔
579

580
    // ====================================================================================================
581
    // Getters
582
    // ====================================================================================================
583
    /**
584
     * The resolved server executable path being used
585
     * @return the path
586
     */
587
    public String getExecutablePath() {
588
        return _executablePath;
1✔
589
    }
590

591
    /**
592
     * Get the port number. Useful if it was automatically assigned
593
     * @return the port number
594
     */
595
    public int getPort() {
596
        return getUserPort();
1✔
597
    }
598

599
    public int getUserPort() {
600
        return _ports.get(USER_PORT_KEY);
1✔
601
    }
602

603
    public int getNatsPort() {
604
        return _ports.get(NATS_PORT_KEY);
1✔
605
    }
606

607
    public int getConfigPort() {
608
        return _ports.get(CONFIG_PORT_KEY);
1✔
609
    }
610

611
    public int getNonNatsPort() {
612
        return _ports.get(NON_NATS_PORT_KEY);
1✔
613
    }
614

615
    public Integer getPort(String key) {
616
        return _ports.get(key);
1✔
617
    }
618

619
    /**
620
     * Get the absolute path of the config file
621
     * @return the path
622
     */
623
    public String getConfigFile() {
624
        return _configFile.getAbsolutePath();
1✔
625
    }
626

627
    /**
628
     * Get the uri in the form nats://localhost:port
629
     * @return the uri string
630
     */
631
    public String getURI() {
632
        return getNatsLocalhostUri(getNatsPort());
1✔
633
    }
634

635
    /**
636
     * Get the command line used to start the server
637
     * @return the command line
638
     */
639
    public String getCmdLine() {
640
        return _cmdLine;
1✔
641
    }
642

643
    /**
644
     * Shut the server down
645
     * @param wait whether to block while waiting for the process to shut down
646
     * @throws InterruptedException if the wait was interrupted
647
     */
648
    public void shutdown(boolean wait) throws InterruptedException {
649
        if (process != null) {
1✔
650
            process.destroy();
1✔
651
            _displayOut.info("%%% Shut down [" + _cmdLine + "]");
1✔
652
            if (wait) {
1✔
653
                process.waitFor();
1✔
654
            }
655
            process = null;
1✔
656
        }
657
    }
1✔
658

659
    /**
660
     * Shut the server down, waiting (blocking)
661
     * @throws InterruptedException if the wait was interrupted
662
     */
663
    public void shutdown() throws InterruptedException {
664
        shutdown(true);
1✔
665
    }
1✔
666

667
    /**
668
     * For AutoCloseable, calls shutdown(true).
669
     */
670
    @Override
671
    public void close() throws Exception {
672
        shutdown(true);
1✔
673
    }
1✔
674

675
    // ====================================================================================================
676
    // Builder
677
    // ====================================================================================================
678
    public static class Builder {
1✔
679
        Map<String, Integer> ports = new HashMap<>();
1✔
680
        DebugLevel debugLevel;
681
        boolean jetstream;
682
        Path configFilePath;
683
        List<String> configInserts;
684
        List<String> customArgs;
685
        Path executablePath;
686
        Output output;
687
        Level outputLevel;
688
        Long processCheckWait;
689
        Integer processCheckTries;
690
        Long connectCheckWait;
691
        Integer connectCheckTries;
692
        boolean fullErrorReportOnStartup = true;
1✔
693

694
        public Builder port(Integer port) {
695
            return port(CONFIG_PORT_KEY, port);
1✔
696
        }
697

698
        public Builder port(String key, Integer port) {
699
            if (port == null) {
1✔
700
                ports.remove(key);
1✔
701
            }
702
            else {
703
                ports.put(key, port);
1✔
704
            }
705
            return this;
1✔
706
        }
707

708
        public Builder ports(Map<String, Integer> ports) {
709
            this.ports.clear();
1✔
710
            if (ports != null) {
1✔
711
                this.ports.putAll(ports);
1✔
712
            }
713
            return this;
1✔
714
        }
715

716
        public Builder debugLevel(DebugLevel debugLevel) {
717
            this.debugLevel = debugLevel;
1✔
718
            return this;
1✔
719
        }
720

721
        public Builder debug(boolean trueForDebugTraceFalseForNoDebug) {
722
            this.debugLevel = trueForDebugTraceFalseForNoDebug ? DebugLevel.DEBUG_TRACE : null;
1✔
723
            return this;
1✔
724
        }
725

726
        public Builder jetstream() {
727
            this.jetstream = true;
1✔
728
            return this;
1✔
729
        }
730

731
        public Builder jetstream(boolean jetStream) {
732
            this.jetstream = jetStream;
1✔
733
            return this;
1✔
734
        }
735

736
        public Builder configFilePath(String configFilePath) {
737
            this.configFilePath = configFilePath == null ? null : Paths.get(configFilePath);
1✔
738
            return this;
1✔
739
        }
740

741
        public Builder configFilePath(Path configFilePath) {
742
            this.configFilePath = configFilePath;
1✔
743
            return this;
1✔
744
        }
745

746
        public Builder configInserts(List<String> configInserts) {
747
            this.configInserts = configInserts == null || configInserts.isEmpty() ? null : configInserts;
1✔
748
            return this;
1✔
749
        }
750

751
        public Builder configInserts(String[] configInserts) {
752
            this.configInserts = configInserts == null || configInserts.length == 0 ? null : Arrays.asList(configInserts);
1✔
753
            return this;
1✔
754
        }
755

756
        public Builder customArgs(List<String> customArgs) {
757
            this.customArgs = customArgs == null || customArgs.isEmpty() ? null : customArgs;
1✔
758
            return this;
1✔
759
        }
760

761
        public Builder customArgs(String[] customArgs) {
762
            this.customArgs = customArgs == null || customArgs.length == 0 ? null : Arrays.asList(customArgs);
1✔
763
            return this;
1✔
764
        }
765

766
        public Builder executablePath(String executablePath) {
767
            this.executablePath = executablePath == null ? null : Paths.get(executablePath);
1✔
768
            return this;
1✔
769
        }
770

771
        public Builder executablePath(Path executablePath) {
772
            this.executablePath = executablePath;
1✔
773
            return this;
1✔
774
        }
775

776
        public Builder output(Output output) {
777
            this.output = output;
1✔
778
            return this;
1✔
779
        }
780

781
        public Builder outputLogger(Logger logger) {
782
            this.output = logger == null ? null : new LoggingOutput(logger);
1✔
783
            return this;
1✔
784
        }
785

786
        public Builder outputLevel(Level level) {
787
            this.outputLevel = level;
1✔
788
            return this;
1✔
789
        }
790

791
        public Builder processCheckWait(Long processWait) {
792
            this.processCheckWait = processWait;
1✔
793
            return this;
1✔
794
        }
795

796
        public Builder processCheckTries(Integer processCheckTries) {
797
            this.processCheckTries = processCheckTries;
1✔
798
            return this;
1✔
799
        }
800

801
        public Builder connectCheckWait(Long connectCheckWait) {
802
            this.connectCheckWait = connectCheckWait;
1✔
803
            return this;
1✔
804
        }
805

806
        public Builder connectCheckTries(Integer connectCheckTries) {
807
            this.connectCheckTries = connectCheckTries;
1✔
808
            return this;
1✔
809
        }
810

811
        public Builder fullErrorReportOnStartup(boolean fullErrorReportOnStartup) {
812
            this.fullErrorReportOnStartup = fullErrorReportOnStartup;
1✔
813
            return this;
1✔
814
        }
815

816
        public Builder runnerOptions(NatsServerRunnerOptions nsro) {
817
            port(nsro.port())
1✔
818
                .debugLevel(nsro.debugLevel())
1✔
819
                .jetstream(nsro.jetStream())
1✔
820
                .configFilePath(nsro.configFilePath())
1✔
821
                .configInserts(nsro.configInserts())
1✔
822
                .customArgs(nsro.customArgs())
1✔
823
                .executablePath(nsro.executablePath())
1✔
824
                .outputLogger(nsro.logger())
1✔
825
                .outputLevel(nsro.logLevel());
1✔
826
            return this;
1✔
827
        }
828

829
        public NatsServerRunner build() throws IOException {
830
            return new NatsServerRunner(this);
1✔
831
        }
832

833
        public NatsServerRunnerOptions buildOptions() {
834
            return new NatsServerRunnerOptionsImpl(this);
1✔
835
        }
836
    }
837

838
    // ====================================================================================================
839
    // Runner Wide Setting
840
    // ====================================================================================================
841
    static final Supplier<Output> DefaultLoggingSupplier = () -> new LoggingOutput(Logger.getLogger(NatsServerRunner.class.getName()));
1✔
842

843
    private static Supplier<Output> DefaultOutputSupplier = DefaultLoggingSupplier;
1✔
844
    private static Level DefaultOutputLevel = Level.INFO;
1✔
845
    private static String PreferredServerPath = null;
1✔
846

847
    public static Supplier<Output> getDefaultOutputSupplier() {
848
        return DefaultOutputSupplier;
1✔
849
    }
850

851
    public static void setDefaultOutputSupplier(Supplier<Output> outputSupplier) {
852
        DefaultOutputSupplier = outputSupplier == null ? DefaultLoggingSupplier : outputSupplier;
1✔
853
    }
1✔
854

855
    public static Level getDefaultOutputLevel() {
856
        return DefaultOutputLevel;
1✔
857
    }
858

859
    public static void setDefaultOutputLevel(Level defaultOutputLevel) {
860
        DefaultOutputLevel = defaultOutputLevel;
1✔
861
    }
1✔
862

863
    public static String getPreferredServerPath() {
864
        return PreferredServerPath;
1✔
865
    }
866

867
    public static void setPreferredServerPath(String preferredServerPath) {
868
        PreferredServerPath = preferredServerPath == null || preferredServerPath.length() == 0 ? null : preferredServerPath;
1✔
869
    }
1✔
870

871
    public static void clearPreferredServerPath() {
872
        PreferredServerPath = null;
1✔
873
    }
1✔
874
}
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