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

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

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

64.3
/exist-core/src/main/java/org/exist/jetty/JettyStart.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.jetty;
50

51
import net.jcip.annotations.GuardedBy;
52
import org.apache.logging.log4j.LogManager;
53
import org.apache.logging.log4j.Logger;
54
import org.eclipse.jetty.server.*;
55
import org.eclipse.jetty.server.handler.ContextHandler;
56
import org.eclipse.jetty.server.handler.HandlerWrapper;
57
import org.eclipse.jetty.servlet.ServletContextHandler;
58
import org.eclipse.jetty.servlet.ServletHolder;
59
import org.eclipse.jetty.util.Jetty;
60
import org.eclipse.jetty.util.MultiException;
61
import org.eclipse.jetty.util.component.LifeCycle;
62
import org.eclipse.jetty.util.resource.PathResource;
63
import org.eclipse.jetty.util.resource.Resource;
64
import org.eclipse.jetty.xml.XmlConfiguration;
65
import org.exist.SystemProperties;
66
import org.exist.http.servlets.ExistExtensionServlet;
67
import org.exist.start.CompatibleJavaVersionCheck;
68
import org.exist.start.Main;
69
import org.exist.start.StartException;
70
import org.exist.storage.BrokerPool;
71
import org.exist.util.*;
72
import org.exist.validation.XmlLibraryChecker;
73
import org.exist.xmldb.DatabaseImpl;
74
import org.exist.xmldb.ShutdownListener;
75
import org.xmldb.api.DatabaseManager;
76
import org.xmldb.api.base.Database;
77
import se.softhouse.jargo.Argument;
78
import se.softhouse.jargo.ArgumentException;
79
import se.softhouse.jargo.CommandLineParser;
80

81
import java.io.IOException;
82
import java.io.LineNumberReader;
83
import java.io.Reader;
84
import java.net.*;
85
import java.nio.file.Files;
86
import java.nio.file.Path;
87
import java.nio.file.Paths;
88
import java.util.*;
89
import java.util.stream.Collectors;
90

91
import static org.exist.util.ThreadUtils.newGlobalThread;
92
import static se.softhouse.jargo.Arguments.helpArgument;
93
import static se.softhouse.jargo.Arguments.stringArgument;
94

95
/**
96
 * This class provides a main method to start Jetty with eXist. It registers shutdown
97
 * handlers to cleanly shut down the database and the webserver.
98
 * 
99
 * @author wolf
100
 */
101
public class JettyStart extends Observable implements LifeCycle.Listener {
102

103
    public static final String JETTY_HOME_PROP = "jetty.home";
104
    public static final String JETTY_BASE_PROP = "jetty.base";
105

106
    private static final String JETTY_PROPETIES_FILENAME = "jetty.properties";
107
    private static final Logger logger = LogManager.getLogger(JettyStart.class);
1✔
108

109
    public final static String SIGNAL_STARTING = "jetty starting";
110
    public final static String SIGNAL_STARTED = "jetty started";
111
    public final static String SIGNAL_ERROR = "error";
112

113
    private final static int STATUS_STARTING = 0;
114
    private final static int STATUS_STARTED = 1;
115
    private final static int STATUS_STOPPING = 2;
116
    private final static int STATUS_STOPPED = 3;
117

118
    /* general arguments */
119
    private static final Argument<String> jettyConfigFilePath = stringArgument()
1✔
120
            .description("Path to Jetty Config File")
1✔
121
            .build();
1✔
122
    private static final Argument<String> existConfigFilePath = stringArgument()
1✔
123
            .description("Path to eXist-db Config File")
1✔
124
            .build();
1✔
125
    private static final Argument<?> helpArg = helpArgument("-h", "--help");
1✔
126

127
    @GuardedBy("this") private int status = STATUS_STOPPED;
1✔
128
    @GuardedBy("this") private Optional<Thread> shutdownHookThread = Optional.empty();
1✔
129
    @GuardedBy("this") private int primaryPort = 8080;
1✔
130

131

132
    public static void main(final String[] args) {
133
        try {
134
            CompatibleJavaVersionCheck.checkForCompatibleJavaVersion();
×
135

136
            CommandLineParser
×
137
                    .withArguments(jettyConfigFilePath, existConfigFilePath)
×
138
                    .andArguments(helpArg)
×
139
                    .programName("startup" + (OSUtil.IS_WINDOWS ? ".bat" : ".sh"))
×
140
                    .parse(args);
×
141

142
        } catch (final StartException e) {
×
143
            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
×
144
                System.err.println(e.getMessage());
×
145
            }
146
            System.exit(e.getErrorCode());
×
147
        } catch (final ArgumentException e) {
×
148
            consoleOut(e.getMessageAndUsage().toString());
×
149
            System.exit(SystemExitCodes.INVALID_ARGUMENT_EXIT_CODE);
×
150
        }
151

152
        final JettyStart start = new JettyStart();
×
153
        start.run(args, null);
×
154
    }
×
155

156
    public JettyStart() {
1✔
157
        // Additional checks XML libs @@@@
158
        XmlLibraryChecker.check();
1✔
159
    }
1✔
160

161
    private static void consoleOut(final String msg) {
162
        System.out.println(msg); //NOSONAR this has to go to the console
×
163
    }
×
164

165
    public synchronized void run() {
166
        run(true);
1✔
167
    }
1✔
168

169
    public synchronized void run(final boolean standalone) {
170
        final String jettyProperty = Optional.ofNullable(System.getProperty(JETTY_HOME_PROP))
1✔
171
                .orElseGet(() -> {
1✔
172
                    final Optional<Path> home = ConfigurationHelper.getExistHome();
×
173
                    final Path jettyHome = FileUtils.resolve(home, "tools").resolve("jetty");
×
174
                    final String jettyPath = jettyHome.toAbsolutePath().toString();
×
175
                    System.setProperty(JETTY_HOME_PROP, jettyPath);
×
176
                    return jettyPath;
×
177
                });
178

179
        System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.Slf4jLog");
1✔
180

181
        final Path jettyConfig;
182
        if (standalone) {
1✔
183
            jettyConfig = Paths.get(jettyProperty).normalize().resolve("etc").resolve(Main.STANDALONE_ENABLED_JETTY_CONFIGS);
1✔
184
        } else {
1✔
185
            jettyConfig = Paths.get(jettyProperty).normalize().resolve("etc").resolve(Main.STANDARD_ENABLED_JETTY_CONFIGS);
1✔
186
        }
187
        run(new String[] { jettyConfig.toAbsolutePath().toString() }, null);
1✔
188
    }
1✔
189
    
190
    public synchronized void run(final String[] args, final Observer observer) {
191
        if (args.length == 0) {
1!
192
            logger.error("No configuration file specified!");
×
193
            return;
×
194
        }
195

196
        Path jettyConfig = Paths.get(args[0]).normalize();
1✔
197
        boolean configFromClasspath = false;
1✔
198
        if (Files.notExists(jettyConfig)) {
1!
199
            logger.warn("Configuration file: {} does not exist!", jettyConfig.toAbsolutePath().toString());
×
200

201
            final String jettyConfigFileName = FileUtils.fileName(jettyConfig.getFileName());
×
202
            logger.warn("Fallback... searching for configuration file on classpath: {}!etc/{}", getClass().getPackage().getName(), jettyConfigFileName);
×
203

204
            final URL jettyConfigUrl = getClass().getResource("etc/" + jettyConfigFileName);
×
205
            if (jettyConfigUrl != null) {
×
206
                try {
207
                    jettyConfig = Paths.get(jettyConfigUrl.toURI()).normalize();
×
208
                    configFromClasspath = true;
×
209
                } catch (final URISyntaxException e) {
×
210
                    logger.error("Unable to retrieve configuration file from classpath: {}", e.getMessage(), e);
×
211
                    return;
×
212
                }
213
            } else {
214
                logger.error("Unable to find configuration file on classpath!");
×
215
                return;
×
216
            }
217
        }
218

219
        final Map<String, String> configProperties;
220
        try {
221
            configProperties = getConfigProperties(jettyConfig.getParent());
1✔
222

223
            // modify JETTY_HOME and JETTY_BASE properties when running with classpath config
224
            if (configFromClasspath) {
1!
225
                final String jettyClasspathHome = jettyConfig.getParent().getParent().toAbsolutePath().toString();
×
226
                System.setProperty(JETTY_HOME_PROP, jettyClasspathHome);
×
227
                configProperties.put(JETTY_HOME_PROP, jettyClasspathHome);
×
228
                configProperties.put(JETTY_BASE_PROP, jettyClasspathHome);
×
229
            }
230

231
            if (observer != null) {
1!
232
                addObserver(observer);
×
233
            }
234

235
            logger.info("Running with Java {} [{} ({}) in {}]",
1✔
236
                System.getProperty("java.version", "(unknown java.version)"),
1✔
237
                System.getProperty("java.vendor", "(unknown java.vendor)"),
1✔
238
                System.getProperty("java.vm.name", "(unknown java.vm.name)"),
1✔
239
                System.getProperty("java.home", "(unknown java.home)")
1✔
240
            );
241

242
            logger.info("Approximate maximum amount of memory for JVM: {}", FileUtils.humanSize(Runtime.getRuntime().maxMemory()));
1✔
243
            logger.info("Number of processors available to JVM: {}", Runtime.getRuntime().availableProcessors());
1✔
244

245
            logger.info("Running as user '{}'", System.getProperty("user.name", "(unknown user.name)"));
1✔
246
            logger.info("[eXist Home : {}]", System.getProperty("exist.home", "unknown"));
1✔
247
            logger.info("[eXist Version : {}]", SystemProperties.getInstance().getSystemProperty("product-version", "unknown"));
1✔
248
            logger.info("[eXist Build : {}]", SystemProperties.getInstance().getSystemProperty("product-build", "unknown"));
1✔
249
            logger.info("[Git commit : {}]", SystemProperties.getInstance().getSystemProperty("git-commit", "unknown"));
1✔
250
            logger.info("[Git commit timestamp : {}]", SystemProperties.getInstance().getSystemProperty("git-commit-timestamp", "unknown"));
1✔
251

252
            logger.info("[Operating System : {} {} {}]", System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch"));
1✔
253
            logger.info("[log4j.configurationFile : {}]", System.getProperty("log4j.configurationFile"));
1✔
254
            logger.info("[jetty Version: {}]", Jetty.VERSION);
1✔
255
            logger.info("[{} : {}]", JETTY_HOME_PROP, configProperties.get(JETTY_HOME_PROP));
1✔
256
            logger.info("[{} : {}]", JETTY_BASE_PROP, configProperties.get(JETTY_BASE_PROP));
1✔
257
            logger.info("[jetty configuration : {}]", jettyConfig.toAbsolutePath().toString());
1✔
258

259
            // configure the database instance
260
            SingleInstanceConfiguration config;
261
            if (args.length == 2) {
1!
262
                config = new SingleInstanceConfiguration(args[1]);
×
263
            } else {
×
264
                config = new SingleInstanceConfiguration();
1✔
265
            }
266
            logger.info("Configuring Elemental from {}",
1✔
267
                    config.getConfigFilePath()
1✔
268
                        .map(Path::normalize).map(Path::toAbsolutePath).map(Path::toString)
1✔
269
                        .orElse("<UNKNOWN>"));
1✔
270

271
            BrokerPool.configure(1, 5, config, Optional.ofNullable(observer));
1✔
272

273
            // register the XMLDB driver
274
            final Database xmldb = new DatabaseImpl();
1✔
275
            xmldb.setProperty("create-database", "false");
1✔
276
            DatabaseManager.registerDatabase(xmldb);
1✔
277

278
        } catch (final Exception e) {
1✔
279
            logger.error("configuration error: {}", e.getMessage(), e);
×
280
            e.printStackTrace();
×
281
            return;
×
282
        }
283

284
        try {
285
            // load jetty configurations
286
            final List<Path> configFiles = getEnabledConfigFiles(jettyConfig);
1✔
287
            final List<Object> configuredObjects = new ArrayList<>();
1✔
288
            XmlConfiguration last = null;
1✔
289
            for(final Path confFile : configFiles) {
1✔
290
                logger.info("[loading jetty configuration : {}]", confFile.toString());
1✔
291
                final Resource resource = new PathResource(confFile);
1✔
292
                final XmlConfiguration configuration = new XmlConfiguration(resource);
1✔
293
                if (last != null) {
1✔
294
                    configuration.getIdMap().putAll(last.getIdMap());
1✔
295
                }
296
                configuration.getProperties().putAll(configProperties);
1✔
297
                configuredObjects.add(configuration.configure());
1✔
298
                last = configuration;
1✔
299
            }
300

301
            // start Jetty
302
            final Optional<Server> maybeServer = startJetty(configuredObjects);
1✔
303
            if(!maybeServer.isPresent()) {
1!
304
                logger.error("Unable to find a server to start in jetty configurations");
×
305
                throw new IllegalStateException();
×
306
            }
307

308
            final Server server = maybeServer.get();
1✔
309

310
            final Connector[] connectors = server.getConnectors();
1✔
311

312
            // Construct description of all ports opened.
313
            final StringBuilder allPorts = new StringBuilder();
1✔
314

315
            if (connectors.length > 1) {
1!
316
                // plural s
317
                allPorts.append("s");
1✔
318
            }
319

320
            boolean establishedPrimaryPort = false;
1✔
321
            for(final Connector connector : connectors) {
1✔
322
                if(connector instanceof NetworkConnector networkConnector) {
1!
323

324
                    if(!establishedPrimaryPort) {
1✔
325
                        this.primaryPort = networkConnector.getLocalPort();
1✔
326
                        establishedPrimaryPort = true;
1✔
327
                    }
328

329
                    allPorts.append(" ");
1✔
330
                    allPorts.append(networkConnector.getLocalPort());
1✔
331
                }
332
            }
333
            
334
            //*************************************************************
335
            final List<URI> serverUris = getSeverURIs(server);
1✔
336
            if(!serverUris.isEmpty()) {
1!
337
                this.primaryPort = serverUris.get(0).getPort();
1✔
338

339
            }
340
            logger.info("-----------------------------------------------------");
1✔
341
            logger.info("Server has started, listening on:");
1✔
342
            for(final URI serverUri : serverUris) {
1✔
343
                logger.info("{}", serverUri.resolve("/"));
1✔
344
            }
345

346
            logger.info("Configured contexts:");
1✔
347
            final LinkedHashSet<Handler> handlers = getAllHandlers(server.getHandler());
1✔
348
            for (final Handler handler: handlers) {
1✔
349
                
350
                if (handler instanceof ContextHandler) {
1✔
351
                    final ContextHandler contextHandler = (ContextHandler) handler;
1✔
352
                    logger.info("{} ({})", contextHandler.getContextPath(), contextHandler.getDisplayName());
1✔
353
                }
354

355
                if (handler instanceof ServletContextHandler) {
1✔
356
                    final ServletContextHandler contextHandler = (ServletContextHandler) handler;
1✔
357
                    final ServiceLoader<ExistExtensionServlet> services = ServiceLoader.load(ExistExtensionServlet.class);
1✔
358

359
                    for (ExistExtensionServlet existExtensionServlet : services) {
1!
360
                        final String pathSpec = existExtensionServlet.getPathSpec();
×
361
                        final String contextPath = contextHandler.getContextPath();
×
362

363
                        // Avoid "//" as logged prefix
364
                        final String normalizedPath = "/".equals(contextPath)
×
365
                                ? pathSpec
×
366
                                : contextPath + pathSpec;
×
367

368
                        logger.info("{} ({})", normalizedPath, existExtensionServlet.getServletInfo());
×
369

370
                        // Register servlet
371
                        contextHandler.addServlet(new ServletHolder(existExtensionServlet), pathSpec);
×
372
                    }
373
                }
374
            }
375

376
            logger.info("-----------------------------------------------------");
1✔
377

378
            setChanged();
1✔
379
            notifyObservers(SIGNAL_STARTED);
1✔
380
            
381
        } catch (final MultiException e) {
1✔
382

383
            // Mute the BindExceptions
384

385
            boolean hasBindException = false;
×
386
            for (final Throwable t : e.getThrowables()) {
×
387
                if (t instanceof java.net.BindException) {
×
388
                    hasBindException = true;
×
389
                    logger.error("----------------------------------------------------------");
×
390
                    logger.error("ERROR: Could not bind to port because {}", t.getMessage());
×
391
                    logger.error(t.toString());
×
392
                    logger.error("----------------------------------------------------------");
×
393
                }
394
            }
395

396
            // If it is another error, print stacktrace
397
            if (!hasBindException) {
×
398
                e.printStackTrace();
×
399
            }
400
            setChanged();
×
401
            notifyObservers(SIGNAL_ERROR);
×
402
            
403
        } catch (final SocketException e) {
×
404
            logger.error("----------------------------------------------------------");
×
405
            logger.error("ERROR: Could not bind to port because {}", e.getMessage());
×
406
            logger.error(e.toString());
×
407
            logger.error("----------------------------------------------------------");
×
408
            setChanged();
×
409
            notifyObservers(SIGNAL_ERROR);
×
410
            
411
        } catch (final Exception e) {
×
412
            e.printStackTrace();
×
413
            setChanged();
×
414
            notifyObservers(SIGNAL_ERROR);
×
415
        }
416
    }
1✔
417

418
    private LinkedHashSet<Handler> getAllHandlers(final Handler handler) {
419
        if(handler instanceof HandlerWrapper handlerWrapper) {
1✔
420
            final LinkedHashSet<Handler> handlers = new LinkedHashSet<>();
1✔
421
            handlers.add(handlerWrapper);
1✔
422
            if(handlerWrapper.getHandler() != null) {
1✔
423
                handlers.addAll(getAllHandlers(handlerWrapper.getHandler()));
1✔
424
            }
425
            return handlers;
1✔
426

427
        } else if(handler instanceof HandlerContainer handlerContainer) {
1✔
428
            final LinkedHashSet<Handler> handlers = new LinkedHashSet<>();
1✔
429
            handlers.add(handler);
1✔
430
            for(final Handler childHandler : handlerContainer.getChildHandlers()) {
1✔
431
                handlers.addAll(getAllHandlers(childHandler));
1✔
432
            }
433
            return handlers;
1✔
434

435
        } else {
436
            //assuming just Handler
437
            final LinkedHashSet<Handler> handlers = new LinkedHashSet<>();
1✔
438
            handlers.add(handler);
1✔
439
            return handlers;
1✔
440
        }
441
    }
442

443
    /**
444
     * See {@link Server#getURI()}
445
     */
446
    private List<URI> getSeverURIs(final Server server) {
447
        final ContextHandler context = server.getChildHandlerByClass(ContextHandler.class);
1✔
448
        return Arrays.stream(server.getConnectors())
1✔
449
                .filter(connector -> connector instanceof NetworkConnector)
1✔
450
                .map(connector -> (NetworkConnector)connector)
1✔
451
                .map(networkConnector -> getURI(networkConnector, context))
1✔
452
                .filter(Objects::nonNull)
1✔
453
                .collect(Collectors.toList());
1✔
454
    }
455

456
    /**
457
     * See {@link Server#getURI()}
458
     */
459
    private URI getURI(final NetworkConnector networkConnector, final ContextHandler context) {
460
        try {
461
            final String protocol = networkConnector.getDefaultConnectionFactory().getProtocol();
1✔
462
            final String scheme;
463
            if (protocol.startsWith("SSL-") || protocol.equals("SSL")) {
1!
464
                scheme = "https";
1✔
465
            } else {
1✔
466
                scheme = "http";
1✔
467
            }
468

469
            String host = null;
1✔
470
            if (context != null && context.getVirtualHosts() != null && context.getVirtualHosts().length > 0) {
1!
471
                host = context.getVirtualHosts()[0];
×
472
            } else {
×
473
                host = networkConnector.getHost();
1✔
474
            }
475

476
            if (host == null) {
1!
477
                host = InetAddress.getLocalHost().getHostAddress();
1✔
478
            }
479

480
            String path = context == null ? null : context.getContextPath();
1!
481
            if (path == null) {
1!
482
                path = "/";
×
483
            }
484
            return new URI(scheme, null, host, networkConnector.getLocalPort(), path, null, null);
1✔
485
        }  catch(final UnknownHostException | URISyntaxException e) {
×
486
            logger.warn(e);
×
487
            return null;
×
488
        }
489
    }
490

491
    private Optional<Server> startJetty(final List<Object> configuredObjects) throws Exception {
492
        // For all objects created by XmlConfigurations, start them if they are lifecycles.
493
        Optional<Server> server = Optional.empty();
1✔
494
        for (final Object configuredObject : configuredObjects) {
1✔
495
            if(configuredObject instanceof Server _server) {
1✔
496

497
                //skip this server if we have already started it
498
                if(server.map(configuredServer -> configuredServer == _server).orElse(false)) {
1!
499
                    continue;
1✔
500
                }
501

502
                //setup server shutdown
503
                _server.addEventListener(this);
1✔
504
                BrokerPool.getInstance().registerShutdownListener(new ShutdownListenerImpl(_server));
1✔
505

506
                // register a shutdown hook for the server
507
                final BrokerPoolAndJettyShutdownHook brokerPoolAndJettyShutdownHook =
1✔
508
                        new BrokerPoolAndJettyShutdownHook(_server);
1✔
509
                final Thread shutdownHookThread = newGlobalThread("BrokerPoolsAndJetty.ShutdownHook", brokerPoolAndJettyShutdownHook);
1✔
510
                this.shutdownHookThread = Optional.of(shutdownHookThread);
1✔
511

512
                try {
513
                    Runtime.getRuntime().addShutdownHook(shutdownHookThread);
1✔
514
                    logger.debug("BrokerPoolsAndJetty.ShutdownHook hook registered");
1✔
515
                } catch (final IllegalArgumentException | IllegalStateException e) {
1✔
516
                    // Hook already registered, or Shutdown in progress
517
                    logger.error("Unable to add BrokerPoolsAndJetty.ShutdownHook hook: {}", e.getMessage(), e);
×
518
                    throw e;
×
519
                }
520

521
                server = Optional.of(_server);
1✔
522
            }
523

524
            if (configuredObject instanceof LifeCycle lc) {
1✔
525
                if (!lc.isRunning()) {
1✔
526
                    logger.info("[Starting jetty component : {}]", lc.getClass().getName());
1✔
527
                    lc.start();
1✔
528
                }
529
            }
530
        }
531

532
        return server;
1✔
533
    }
534

535
    private Map<String, String> getConfigProperties(final Path configDir) throws IOException {
536
        final Map<String, String> configProperties = new HashMap<>();
1✔
537

538
        //load jetty.properties file
539
        final Path propertiesFile = configDir.resolve(JETTY_PROPETIES_FILENAME);
1✔
540
        if(Files.exists(propertiesFile)) {
1!
541
            final Properties jettyProperties = new Properties();
×
542
            try(final Reader reader = Files.newBufferedReader(propertiesFile)) {
×
543
                jettyProperties.load(reader);
×
544
                logger.info("Loaded jetty.properties from: {}", propertiesFile.toAbsolutePath().toString());
×
545

546
                for(final Map.Entry<Object, Object> property : jettyProperties.entrySet()) {
×
547
                    configProperties.put(property.getKey().toString(), property.getValue().toString());
×
548
                }
549
            }
550
        }
551

552
        // set or override jetty.home and jetty.base with System properties
553
        configProperties.put(JETTY_HOME_PROP, System.getProperty(JETTY_HOME_PROP));
1✔
554
        configProperties.put(JETTY_BASE_PROP, System.getProperty(JETTY_BASE_PROP, System.getProperty(JETTY_HOME_PROP)));
1✔
555

556
        return configProperties;
1✔
557
    }
558

559
    private List<Path> getEnabledConfigFiles(final Path enabledJettyConfigs) throws IOException {
560
        if(Files.notExists(enabledJettyConfigs)) {
1!
561
            throw new IOException("Cannot find config enabler: "  + enabledJettyConfigs.toString());
×
562
        } else {
563
            final List<Path> configFiles = new ArrayList<>();
1✔
564
            try (final LineNumberReader reader = new LineNumberReader(Files.newBufferedReader(enabledJettyConfigs))) {
1✔
565
                String line = null;
1✔
566
                while ((line = reader.readLine()) != null) {
1✔
567
                    final String tl = line.trim();
1✔
568
                    if (tl.isEmpty() || tl.charAt(0) == '#') {
1✔
569
                        continue;
1✔
570
                    } else {
571
                        final Path configFile = enabledJettyConfigs.getParent().resolve(tl);
1✔
572
                        if (Files.notExists(configFile)) {
1!
573
                            throw new IOException("Cannot find enabled config: " + configFile.toString());
×
574
                        } else {
575
                            configFiles.add(configFile);
1✔
576
                        }
577
                    }
578
                }
579
            }
580
            return configFiles;
1✔
581
        }
582
    }
583

584
    public synchronized void shutdown() {
585
        shutdownHookThread.ifPresent(thread -> {
1✔
586
            try {
587
                Runtime.getRuntime().removeShutdownHook(thread);
1✔
588
                logger.debug("BrokerPoolsAndJetty.ShutdownHook hook unregistered");
1✔
589
            } catch (final IllegalStateException e) {
1✔
590
                // Shutdown in progress
591
                logger.warn("Unable to remove BrokerPoolsAndJetty.ShutdownHook hook: {}", e.getMessage());
×
592
            }
593
        });
1✔
594
        
595
        BrokerPool.stopAll(false);
1✔
596
        
597
        while (status != STATUS_STOPPED) {
1✔
598
            try {
599
                wait();
1✔
600
            } catch (final InterruptedException e) {
1✔
601
                // ignore
602
            }
603
        }
604
    }
1✔
605

606
    /**
607
     * This class gets called after the database received a shutdown request.
608
     *
609
     * @author wolf
610
     */
611
    private static class ShutdownListenerImpl implements ShutdownListener {
612
        private final Server server;
613

614
        ShutdownListenerImpl(final Server server) {
1✔
615
            this.server = server;
1✔
616
        }
1✔
617

618
        @Override
619
        public void shutdown(final String dbname, final int remainingInstances) {
620
            logger.info("Database shutdown: stopping server in 1sec ...");
1✔
621
            if (remainingInstances == 0) {
1!
622
                // give the webserver a 1s chance to complete open requests
623
                final Timer timer = new Timer("jetty shutdown schedule", true);
1✔
624
                timer.schedule(new TimerTask() {
1✔
625
                    @Override
626
                    public void run() {
627
                        try {
628
                            // stop the server
629
                            server.stop();
1✔
630
                            server.join();
1✔
631

632
                            // make sure to stop the timer thread!
633
                            timer.cancel();
1✔
634
                        } catch (final Exception e) {
1✔
635
                            e.printStackTrace();
×
636
                        }
637
                    }
1✔
638
                }, 1000); // timer.schedule
1✔
639
            }
640
        }
1✔
641
    }
642

643
    private static class BrokerPoolAndJettyShutdownHook implements Runnable {
644
        private final Server server;
645

646
        BrokerPoolAndJettyShutdownHook(final Server server) {
1✔
647
            this.server = server;
1✔
648
        }
1✔
649

650
        @Override
651
        public void run() {
652
            BrokerPool.stopAll(true);
×
653
            if (server.isStopping() || server.isStopped()) {
×
654
                return;
×
655
            }
656

657
            try {
658
                server.stop();
×
659
            } catch (final Exception e) {
×
660
                e.printStackTrace();
×
661
            }
662
        }
×
663
    }
664

665
    public synchronized boolean isStarted() {
666
        if (status == STATUS_STARTED || status == STATUS_STARTING) {
×
667
            return true;
×
668
        }
669
        if (status == STATUS_STOPPED) {
×
670
            return false;
×
671
        }
672
        while (status != STATUS_STOPPED) {
×
673
            try {
674
                wait();
×
675
            } catch (final InterruptedException e) {
×
676
            }
677
        }
678
        return false;
×
679
    }
680

681
    @Override
682
    public synchronized void lifeCycleStarting(final LifeCycle lifeCycle) {
683
        logger.info("Jetty server starting...");
1✔
684
        setChanged();
1✔
685
        notifyObservers(SIGNAL_STARTING);
1✔
686
        status = STATUS_STARTING;
1✔
687
        notifyAll();
1✔
688
    }
1✔
689

690
    @Override
691
    public synchronized void lifeCycleStarted(final LifeCycle lifeCycle) {
692
        logger.info("Jetty server started.");
1✔
693
        setChanged();
1✔
694
        notifyObservers(SIGNAL_STARTED);
1✔
695
        status = STATUS_STARTED;
1✔
696
        notifyAll();
1✔
697
    }
1✔
698

699
    @Override
700
    public void lifeCycleFailure(final LifeCycle lifeCycle, final Throwable throwable) {
701
    }
×
702

703
    @Override
704
    public synchronized void lifeCycleStopping(final LifeCycle lifeCycle) {
705
        logger.info("Jetty server stopping...");
1✔
706
        status = STATUS_STOPPING;
1✔
707
        notifyAll();
1✔
708
    }
1✔
709

710
    @Override
711
    public synchronized void lifeCycleStopped(final LifeCycle lifeCycle) {
712
        logger.info("Jetty server stopped");
1✔
713
        status = STATUS_STOPPED;
1✔
714
        notifyAll();
1✔
715
    }
1✔
716

717
    public synchronized int getPrimaryPort() {
718
        return primaryPort;
1✔
719
    }
720
}
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