Skip to content

Simplify startup code #5701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[refactor] improve readability of startup classes
  • Loading branch information
dizzzz committed Apr 13, 2025
commit 48237ab1dbb28959860047cfe568f7f851340b5a
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public void start() throws Exception {
runArgs[0] = MODE_JETTY;
System.arraycopy(args, 0, runArgs, 1, args.length);

this.main.runEx(runArgs);
this.main.startExistdb(runArgs);
}

@Override
public void stop() throws Exception {
this.main.shutdownEx();
this.main.shutdownExistdb();
}

@Override
Expand Down
279 changes: 146 additions & 133 deletions exist-start/src/main/java/org/exist/start/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,21 @@ public class Main {
public static final String PROP_XML_CATALOG_ALWAYS_RESOLVE = "xml.catalog.alwaysResolve";
public static final String STANDALONE_ENABLED_JETTY_CONFIGS = "standalone.enabled-jetty-configs";
public static final String STANDARD_ENABLED_JETTY_CONFIGS = "standard.enabled-jetty-configs";
public static final String MODE_JETTY = "jetty";
public static final String MODE_STANDALONE = "standalone";
public static final String MODE_OTHER = "other";
public static final String MODE_CLIENT = "client";
public static final String MODE_BACKUP = "backup";
static final int ERROR_CODE_INCOMPATIBLE_JAVA_DETECTED = 13;
private static final int ERROR_CODE_GENERAL = 1;
private static final int ERROR_CODE_NO_JETTY_CONFIG = 7;
private static final String PROP_EXIST_START_DEBUG = "exist.start.debug";
private static final String PROP_JAVA_TEMP_DIR = "java.io.tmpdir";
private static final String PROP_JUL_MANAGER = "java.util.logging.manager";
private static final String PROP_LOG4J_CONFIGURATION_FILE = "log4j.configurationFile";

private static Main exist;
private final boolean _debug = Boolean.getBoolean(PROP_EXIST_START_DEBUG);
private String _mode = "jetty";
private final boolean inDebugMode = Boolean.getBoolean(PROP_EXIST_START_DEBUG);
private String _mode = MODE_JETTY;

private Main() {
}
Expand Down Expand Up @@ -115,21 +119,8 @@ public static Main getMain() {
return exist;
}

private static Path getDirectory(final String name) {
try {
if (name != null) {
final Path dir = Paths.get(name).normalize().toAbsolutePath();
if (Files.isDirectory(dir)) {
return dir;
}
}
} catch (final InvalidPathException e) {
// NOP
}
return null;
}

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

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

Expand All @@ -143,13 +134,48 @@ private static void invokeMain(final ClassLoader classloader, final String class
main.invoke(null, method_params);
}

public String getMode() {
return this._mode;
private static EXistClassLoader getEXistClassLoader() {
final Classpath _classpath = new Classpath();
final EXistClassLoader eXistClassLoader = _classpath.getClassLoader(null);
Thread.currentThread().setContextClassLoader(eXistClassLoader);
return eXistClassLoader;
}

private static void setupLog4j2(final Optional<Path> existHomeDir) {
// find log4j2.xml
Optional<Path> log4jConfigurationFile = Optional.ofNullable(System.getProperty(PROP_LOG4J_CONFIGURATION_FILE)).map(Paths::get);
if (log4jConfigurationFile.isEmpty()) {
if (existHomeDir.isPresent() && Files.exists(existHomeDir.get().resolve(CONFIG_DIR_NAME))) {
log4jConfigurationFile = existHomeDir.map(f -> f.resolve(CONFIG_DIR_NAME).resolve("log4j2.xml"));
}

if (log4jConfigurationFile.isPresent() && Files.isReadable(log4jConfigurationFile.get())) {
System.setProperty(PROP_LOG4J_CONFIGURATION_FILE, log4jConfigurationFile.get().toAbsolutePath().toString());
}
}

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

// Enable JMX support log4j since v2.24.0 [2024]
System.setProperty(PROP_LOG4J_DISABLEJMX, "false");
}

private String getMode() {
return _mode;
}

private void setMode(final String mode) {
_mode = mode;
}

public void run(final String[] args) {
try {
runEx(args);
startExistdb(args);
} catch (final StartException e) {
if (e.getMessage() != null && !e.getMessage().isEmpty()) {
System.err.println(e.getMessage());
Expand All @@ -158,120 +184,95 @@ public void run(final String[] args) {
}
}

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

// Check if the OpenJDK version can corrupt eXist-db
CompatibleJavaVersionCheck.checkForCompatibleJavaVersion();

final String _classname;
if (args.length > 0) {
switch (args[0]) {
case "client" -> {
_classname = "org.exist.client.InteractiveClient";
_mode = "client";
}
case "backup" -> {
_classname = "org.exist.backup.Main";
_mode = "backup";
}
case "jetty", "standalone" -> {
_classname = "org.exist.jetty.JettyStart";
_mode = args[0];
}
case "launch" -> {
_classname = "org.exist.launcher.LauncherWrapper";
_mode = "jetty";
}
case "launcher" -> {
_classname = "org.exist.launcher.LauncherWrapper";
_mode = "other";
}
case "shutdown" -> {
_classname = "org.exist.jetty.ServerShutdown";
_mode = "other";
}
case null, default -> {
_classname = args[0];
_mode = "other";
}
}

final String[] nargs = new String[args.length - 1];
if (args.length > 1) {
System.arraycopy(args, 1, nargs, 0, args.length - 1);
}
args = nargs;
_classname = getClassName(args);

} else {
_classname = "org.exist.launcher.LauncherWrapper";
_mode = "other";
setMode(MODE_OTHER);
}

if (_debug) {
System.err.println("mode=" + _mode);
if (inDebugMode) {
System.err.println("mode=" + getMode());
}

// copy all elements except 1st one
final String[] newArguments = new String[args.length - 1];
if (args.length > 1) {
System.arraycopy(args, 1, newArguments, 0, args.length - 1);
}
args = newArguments;

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

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

Optional<Path> existJettyConfigFile = getFromSysPropOrEnv(PROP_EXIST_JETTY_CONFIG, ENV_EXIST_JETTY_CONFIG).map(Paths::get);
if (existJettyConfigFile.isEmpty()) {
final String config;
if ("jetty".equals(_mode)) {
config = STANDARD_ENABLED_JETTY_CONFIGS;
} else {
config = STANDALONE_ENABLED_JETTY_CONFIGS;
}

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

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

if (existJettyConfigFile.isEmpty()) {
System.err.println("ERROR: jetty config file could not be found! Make sure to set exist.jetty.config or EXIST_JETTY_CONFIG.");
System.err.flush();
throw new StartException(ERROR_CODE_NO_JETTY_CONFIG);
}
}
final String[] jettyStartArgs = new String[1 + args.length];
jettyStartArgs[0] = existJettyConfigFile.get().toAbsolutePath().toString();
System.arraycopy(args, 0, jettyStartArgs, 1, args.length);
args = jettyStartArgs;
if (MODE_JETTY.equals(getMode()) || MODE_STANDALONE.equals(getMode())) {
args = configureJetty(args, existHomeDir);
}

// find log4j2.xml
Optional<Path> log4jConfigurationFile = Optional.ofNullable(System.getProperty(PROP_LOG4J_CONFIGURATION_FILE)).map(Paths::get);
if (log4jConfigurationFile.isEmpty()) {
if (existHomeDir.isPresent() && Files.exists(existHomeDir.get().resolve(CONFIG_DIR_NAME))) {
log4jConfigurationFile = existHomeDir.map(f -> f.resolve(CONFIG_DIR_NAME).resolve("log4j2.xml"));
}
setupLog4j2(existHomeDir);

if (log4jConfigurationFile.isPresent() && Files.isReadable(log4jConfigurationFile.get())) {
System.setProperty(PROP_LOG4J_CONFIGURATION_FILE, log4jConfigurationFile.get().toAbsolutePath().toString());
}
}
// Modify behavior XML resolver for > 5.x [2024]
System.setProperty(PROP_XML_CATALOG_ALWAYS_RESOLVE, "false");

if (log4jConfigurationFile.isPresent()) {
//redirect JUL to log4j2 unless otherwise specified
System.setProperty(PROP_JUL_MANAGER,
Optional.ofNullable(System.getProperty(PROP_JUL_MANAGER)).orElse("org.apache.logging.log4j.jul.LogManager"));
}
// Clean up tempdir for Jetty...
tweakTempDirectory();

// Enable JXM support log4j since v2.24.0 [2024]
System.setProperty(PROP_LOG4J_DISABLEJMX, "false");
// Setup classloader
final EXistClassLoader eXistClassLoader = getEXistClassLoader();

// Modify behavior XML resolver for > 5.x [2024]
System.setProperty(PROP_XML_CATALOG_ALWAYS_RESOLVE, "false");
// Invoke main class using new classloader.
try {
invokeMain(eXistClassLoader, _classname, args);
} catch (final Exception e) {
e.printStackTrace();
throw new StartException(ERROR_CODE_GENERAL);
}
}

private String getClassName(final String[] args) {
final String className;
switch (args[0]) {
case MODE_CLIENT -> {
className = "org.exist.client.InteractiveClient";
setMode(MODE_CLIENT);
}
case MODE_BACKUP -> {
className = "org.exist.backup.Main";
setMode(MODE_BACKUP);
}
case MODE_JETTY, MODE_STANDALONE -> {
className = "org.exist.jetty.JettyStart";
setMode(args[0]);
}
case "launch" -> {
className = "org.exist.launcher.LauncherWrapper";
setMode(MODE_JETTY);
}
case "launcher" -> {
className = "org.exist.launcher.LauncherWrapper";
setMode(MODE_OTHER);
}
case "shutdown" -> {
className = "org.exist.jetty.ServerShutdown";
setMode(MODE_OTHER);
}
case null, default -> {
className = args[0];
setMode(MODE_OTHER);
}
}
return className;
}

// clean up tempdir for Jetty...
private void tweakTempDirectory() {
try {
final Path tmpdir = Paths.get(System.getProperty(PROP_JAVA_TEMP_DIR)).toAbsolutePath();
if (Files.isDirectory(tmpdir)) {
Expand All @@ -282,22 +283,42 @@ public void runEx(String[] args) throws StartException {
// ignore
}

if (_debug) {
if (inDebugMode) {
System.err.println(PROP_JAVA_TEMP_DIR + "=" + System.getProperty(PROP_JAVA_TEMP_DIR));
}
}

// setup classloader
final Classpath _classpath = new Classpath();
final EXistClassLoader cl = _classpath.getClassLoader(null);
Thread.currentThread().setContextClassLoader(cl);
private String[] configureJetty(final String[] arguments, final Optional<Path> existHomeDir) throws StartException {
final Optional<Path> jettyHomeDir = getFromSysPropOrEnv(PROP_JETTY_HOME, ENV_JETTY_HOME).map(Paths::get);

// Invoke main class using new classloader.
try {
invokeMain(cl, _classname, args);
} catch (final Exception e) {
e.printStackTrace();
throw new StartException(ERROR_CODE_GENERAL);
Optional<Path> existJettyConfigFile = getFromSysPropOrEnv(PROP_EXIST_JETTY_CONFIG, ENV_EXIST_JETTY_CONFIG).map(Paths::get);
if (existJettyConfigFile.isEmpty()) {
final String config;
if (MODE_JETTY.equals(_mode)) {
config = STANDARD_ENABLED_JETTY_CONFIGS;
} else {
config = STANDALONE_ENABLED_JETTY_CONFIGS;
}

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

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

if (existJettyConfigFile.isEmpty()) {
System.err.println("ERROR: jetty config file could not be found! Make sure to set exist.jetty.config or EXIST_JETTY_CONFIG.");
System.err.flush();
throw new StartException(ERROR_CODE_NO_JETTY_CONFIG);
}
}

final String[] jettyStartArgs = new String[1 + arguments.length];
jettyStartArgs[0] = existJettyConfigFile.get().toAbsolutePath().toString();
System.arraycopy(arguments, 0, jettyStartArgs, 1, arguments.length);
return jettyStartArgs;
}

private Optional<String> getFromSysPropOrEnv(final String sysPropName, final String envVarName) {
Expand All @@ -308,29 +329,21 @@ private Optional<String> getFromSysPropOrEnv(final String sysPropName, final Str
value.ifPresent(s -> System.setProperty(sysPropName, s));
}

if (_debug && value.isPresent()) {
if (inDebugMode && value.isPresent()) {
System.err.println(sysPropName + "=" + System.getProperty(sysPropName));
}

return value;
}

public void shutdown() {
// only used in test suite
try {
shutdownEx();
} catch (final StopException e) {
e.printStackTrace();
}
}

public void shutdownEx() throws StopException {
public void shutdownExistdb() throws StopException {
// only used in test suite
try {
final Class<?> brokerPool = Class.forName("org.exist.storage.BrokerPools");
final Method stopAll = brokerPool.getDeclaredMethod("stopAll", boolean.class);
stopAll.setAccessible(true);
stopAll.invoke(null, false);

} catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
throw new StopException(e.getMessage(), e);
Expand Down
Loading
Loading