• 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

0.0
/exist-start/src/main/java/org/exist/start/Main.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
 * NOTE: Parts of this file contain code from 'Mort Bay Consulting Pty. Ltd.'.
50
 *       The original license header is included below.
51
 *
52
 * =====================================================================
53
 *
54
 * Copyright 2002-2005 Mort Bay Consulting Pty. Ltd.
55
 *
56
 * Licensed under the Apache License, Version 2.0 (the "License");
57
 * you may not use this file except in compliance with the License.
58
 * You may obtain a copy of the License at
59
 * http://www.apache.org/licenses/LICENSE-2.0
60
 * Unless required by applicable law or agreed to in writing, software
61
 * distributed under the License is distributed on an "AS IS" BASIS,
62
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
63
 * See the License for the specific language governing permissions and
64
 * limitations under the License.
65
 */
66
package org.exist.start;
67

68
import java.io.*;
69
import java.lang.reflect.InvocationTargetException;
70
import java.lang.reflect.Method;
71
import java.nio.file.Files;
72
import java.nio.file.InvalidPathException;
73
import java.nio.file.Path;
74
import java.nio.file.Paths;
75
import java.util.*;
76
import java.util.function.Predicate;
77
import java.util.stream.Collectors;
78
import java.util.stream.Stream;
79

80
/**
81
 * This is an adopted version of the corresponding classes shipped
82
 * with Jetty.
83
 *
84
 * @author Jan Hlavaty (hlavac@code.cz)
85
 * @author Wolfgang Meier (meier@ifs.tu-darmstadt.de)
86
 * @version $Revision$
87
 *          TODO:
88
 *          - finish possible jetty.home locations
89
 *          - better handling of errors (i.e. when jetty.home cannot be autodetected...)
90
 *          - include entries from lib _when needed_
91
 */
92
public class Main {
93
    public static final String STANDARD_ENABLED_JETTY_CONFIGS = "standard.enabled-jetty-configs";
94
    public static final String STANDALONE_ENABLED_JETTY_CONFIGS = "standalone.enabled-jetty-configs";
95
    public static final String PROP_LOG4J_DISABLEJMX = "log4j2.disableJmx";
96

97
    private static final int ERROR_CODE_GENERAL = 1;
98
    private static final int ERROR_CODE_NO_JETTY_CONFIG = 7;
99
    static final int ERROR_CODE_INCOMPATIBLE_JAVA_DETECTED = 13;
100

101
    public static final String CONFIG_DIR_NAME = "etc";
102

103
    private static final String PROP_EXIST_START_DEBUG = "exist.start.debug";
104
    public static final String PROP_EXIST_JETTY_CONFIG = "exist.jetty.config";
105
    public static final String PROP_EXIST_HOME = "exist.home";
106
    public static final String PROP_JETTY_HOME = "jetty.home";
107
    private static final String PROP_LOG4J_CONFIGURATION_FILE = "log4j.configurationFile";
108
    private static final String PROP_JUL_MANAGER = "java.util.logging.manager";
109
    private static final String PROP_JAVA_TEMP_DIR = "java.io.tmpdir";
110

111
    public static final String ENV_EXIST_JETTY_CONFIG = "EXIST_JETTY_CONFIG";
112
    public static final String ENV_EXIST_HOME = "EXIST_HOME";
113
    public static final String ENV_JETTY_HOME = "JETTY_HOME";
114

115

116
    private static Main exist;
117

118
    private String _mode = "jetty";
×
119
    private boolean _debug = Boolean.getBoolean(PROP_EXIST_START_DEBUG);
×
120

121
    public static void main(final String[] args) {
122
        try {
123
            getMain().run(args);
×
124

125
        } catch (final Exception e) {
×
126
            e.printStackTrace();
×
127
            System.exit(ERROR_CODE_GENERAL);
×
128
        }
×
129
    }
×
130

131
    /**
132
     * Singleton Factory Method
133
     *
134
     * @return instance of Main class.
135
     */
136
    public static Main getMain() {
137
        if (exist == null) {
×
138
            exist = new Main();
×
139
        }
140
        return exist;
×
141
    }
142

143
    public String getMode() {
144
        return this._mode;
×
145
    }
146

147
    private Main() {
×
148
    }
×
149

150
    public Main(final String mode) {
×
151
        this._mode = mode;
×
152
    }
×
153

154
    private static Path getDirectory(final String name) {
155
        try {
156
            if (name != null) {
×
157
                final Path dir = Paths.get(name).normalize().toAbsolutePath();
×
158
                if (Files.isDirectory(dir)) {
×
159
                    return dir;
×
160
                }
161
            }
162
        } catch (final InvalidPathException e) {
×
163
            // NOP
164
        }
×
165
        return null;
×
166
    }
167

168
    private static void invokeMain(final ClassLoader classloader, final String classname, final String[] args)
169
            throws IllegalAccessException, InvocationTargetException,
170
            NoSuchMethodException, ClassNotFoundException {
171

172
        final Class<?> invoked_class = classloader.loadClass(classname);
×
173

174
        final Class<?>[] method_param_types = new Class[1];
×
175
        method_param_types[0] = args.getClass();
×
176

177
        final Method main = invoked_class.getDeclaredMethod("main", method_param_types);
×
178

179
        final Object[] method_params = new Object[1];
×
180
        method_params[0] = args;
×
181
        main.invoke(null, method_params);
×
182
    }
×
183

184
    public void run(final String[] args) {
185
        try {
186
            runEx(args);
×
187
        } catch (final StartException e) {
×
188
            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
×
189
                System.err.println(e.getMessage());
×
190
            }
191
            System.exit(e.getErrorCode());
×
192
        }
×
193
    }
×
194

195

196

197
    public void runEx(String[] args) throws StartException {
198

199
        // Check if the OpenJDK version can corrupt the database
200
        CompatibleJavaVersionCheck.checkForCompatibleJavaVersion();
×
201

202
        final String _classname;
203
        if (args.length > 0) {
×
204
            if ("client".equals(args[0])) {
×
205
                _classname = "org.exist.client.InteractiveClient";
×
206
                _mode = "client";
×
207

208
            } else if ("backup".equals(args[0])) {
×
209
                _classname = "org.exist.backup.Main";
×
210
                _mode = "backup";
×
211

212
            } else if ("jetty".equals(args[0]) || "standalone".equals(args[0])) {
×
213
                _classname = "org.exist.jetty.JettyStart";
×
214
                _mode = args[0];
×
215

216
            } else if ("launch".equals(args[0])) {
×
217
                _classname = "org.exist.launcher.LauncherWrapper";
×
218
                _mode = "jetty";
×
219

220
            } else if ("launcher".equals(args[0])) {
×
221
                _classname = "org.exist.launcher.LauncherWrapper";
×
222
                _mode = "other";
×
223

224
            } else if ("shutdown".equals(args[0])) {
×
225
                _classname = "org.exist.jetty.ServerShutdown";
×
226
                _mode = "other";
×
227

228
            } else {
229
                _classname = args[0];
×
230
                _mode = "other";
×
231
            }
232

233
            final String[] nargs = new String[args.length - 1];
×
234
            if (args.length > 1) {
×
235
                System.arraycopy(args, 1, nargs, 0, args.length - 1);
×
236
            }
237
            args = nargs;
×
238

239
        } else {
×
240
            _classname = "org.exist.launcher.LauncherWrapper";
×
241
            _mode = "other";
×
242
        }
243

244
        if (_debug) {
×
245
            System.err.println("mode=" + _mode);
×
246
        }
247

248
        // try and figure out exist home dir
249
        final Optional<Path> existHomeDir = getFromSysPropOrEnv(PROP_EXIST_HOME, ENV_EXIST_HOME).map(Paths::get);
×
250

251
        // try to find Jetty
252
        if ("jetty".equals(_mode) || "standalone".equals(_mode)) {
×
253
            final Optional<Path> jettyHomeDir = getFromSysPropOrEnv(PROP_JETTY_HOME, ENV_JETTY_HOME).map(Paths::get);
×
254

255
            Optional<Path> existJettyConfigFile = getFromSysPropOrEnv(PROP_EXIST_JETTY_CONFIG, ENV_EXIST_JETTY_CONFIG).map(Paths::get);
×
256
            if (!existJettyConfigFile.isPresent()) {
×
257
                final String config;
258
                if ("jetty".equals(_mode)) {
×
259
                    config = STANDARD_ENABLED_JETTY_CONFIGS;
×
260
                } else {
261
                    config = STANDALONE_ENABLED_JETTY_CONFIGS;
×
262
                }
263

264
                if (jettyHomeDir.isPresent() && Files.exists(jettyHomeDir.get().resolve(CONFIG_DIR_NAME))) {
×
265
                    existJettyConfigFile = jettyHomeDir.map(f -> f.resolve(CONFIG_DIR_NAME).resolve(config));
×
266
                }
267

268
                if (existHomeDir.isPresent() && Files.exists(existHomeDir.get().resolve(CONFIG_DIR_NAME))) {
×
269
                    existJettyConfigFile = existHomeDir.map(f -> f.resolve(CONFIG_DIR_NAME).resolve(config));
×
270
                }
271

272
                if (!existJettyConfigFile.isPresent()) {
×
273
                    System.err.println("ERROR: jetty config file could not be found! Make sure to set exist.jetty.config or EXIST_JETTY_CONFIG.");
×
274
                    System.err.flush();
×
275
                    throw new StartException(ERROR_CODE_NO_JETTY_CONFIG);
×
276
                }
277
            }
278
            final String[] jettyStartArgs = new String[1 + args.length];
×
279
            jettyStartArgs[0] = existJettyConfigFile.get().toAbsolutePath().toString();
×
280
            System.arraycopy(args, 0, jettyStartArgs, 1, args.length);
×
281
            args = jettyStartArgs;
×
282
        }
283

284
        // find log4j2.xml
285
        Optional<Path> log4jConfigurationFile = Optional.ofNullable(System.getProperty(PROP_LOG4J_CONFIGURATION_FILE)).map(Paths::get);
×
286
        if (!log4jConfigurationFile.isPresent()) {
×
287
            if (existHomeDir.isPresent() && Files.exists(existHomeDir.get().resolve(CONFIG_DIR_NAME))) {
×
288
                log4jConfigurationFile = existHomeDir.map(f -> f.resolve(CONFIG_DIR_NAME).resolve("log4j2.xml"));
×
289
            }
290

291
            if (log4jConfigurationFile.isPresent() && Files.isReadable(log4jConfigurationFile.get())) {
×
292
                System.setProperty(PROP_LOG4J_CONFIGURATION_FILE, log4jConfigurationFile.get().toAbsolutePath().toString());
×
293
            }
294
        }
295

296
        if (log4jConfigurationFile.isPresent()) {
×
297
            //redirect JUL to log4j2 unless otherwise specified
298
            System.setProperty(PROP_JUL_MANAGER, Optional.ofNullable(System.getProperty(PROP_JUL_MANAGER)).orElse("org.apache.logging.log4j.jul.LogManager"));
×
299
        }
300

301
        // Enable JXM support log4j since v2.24.0 [2024]
302
        System.setProperty(PROP_LOG4J_DISABLEJMX, "false");
×
303

304
        // clean up tempdir for Jetty...
305
        try {
306
            final Path tmpdir = Paths.get(System.getProperty(PROP_JAVA_TEMP_DIR)).toAbsolutePath();
×
307
            if (Files.isDirectory(tmpdir)) {
×
308
                System.setProperty(PROP_JAVA_TEMP_DIR, tmpdir.toString());
×
309
            }
310

311
        } catch (final InvalidPathException e) {
×
312
            // ignore
313
        }
×
314

315
        if (_debug) {
×
316
            System.err.println(PROP_JAVA_TEMP_DIR + "=" + System.getProperty(PROP_JAVA_TEMP_DIR));
×
317
        }
318

319
        // setup classloader
320
        final Classpath _classpath = new Classpath();
×
321
        final EXistClassLoader cl = _classpath.getClassLoader(null);
×
322
        Thread.currentThread().setContextClassLoader(cl);
×
323

324
        // Invoke main class using new classloader.
325
        try {
326
            invokeMain(cl, _classname, args);
×
327
        } catch (final Exception e) {
×
328
            e.printStackTrace();
×
329
            throw new StartException(ERROR_CODE_GENERAL);
×
330
        }
×
331
    }
×
332

333
    private Optional<String> getFromSysPropOrEnv(final String sysPropName, final String envVarName) {
334
        Optional<String> value = Optional.ofNullable(System.getProperty(sysPropName));
×
335
        if (!value.isPresent()) {
×
336
            value = Optional.ofNullable(System.getenv().get(envVarName));
×
337
            // if we managed to detect from environment, store it in a system property
338
            value.ifPresent(s -> System.setProperty(sysPropName, s));
×
339
        }
340

341
        if (_debug && value.isPresent()) {
×
342
            System.err.println(sysPropName + "=" + System.getProperty(sysPropName));
×
343
        }
344

345
        return value;
×
346
    }
347

348
    public void shutdown() {
349
        // only used in test suite
350
        try {
351
            shutdownEx();
×
352
        } catch (final StopException e) {
×
353
            e.printStackTrace();
×
354
        }
×
355
    }
×
356

357
    public void shutdownEx() throws StopException {
358
        // only used in test suite
359
        try {
360
            final Class<?> brokerPool = Class.forName("org.exist.storage.BrokerPools");
×
361
            final Method stopAll = brokerPool.getDeclaredMethod("stopAll", boolean.class);
×
362
            stopAll.setAccessible(true);
×
363
            stopAll.invoke(null, false);
×
364
        } catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
×
365
            throw new StopException(e.getMessage(), e);
×
366
        }
×
367
    }
×
368

369
    /**
370
     * Copied from {@link org.exist.util.FileUtils#list(Path, Predicate)}
371
     * as org.exist.start is compiled into a separate Jar and doesn't have
372
     * the rest of eXist available on the classpath
373
     */
374
    static List<Path> list(final Path directory, final Predicate<Path> filter) throws IOException {
375
        try(final Stream<Path> entries = Files.list(directory).filter(filter)) {
×
376
            return entries.collect(Collectors.toList());
×
377
        }
378
    }
379

380
    /**
381
     * Copied from {@link org.exist.util.FileUtils#fileName(Path)}
382
     * as org.exist.start is compiled into a separate Jar and doesn't have
383
     * the rest of eXist available on the classpath
384
     */
385
    static String fileName(final Path path) {
386
        return path.getFileName().toString();
×
387
    }
388
}
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