diff --git a/base/src/main/java/de/staropensource/engine/base/Engine.java b/base/src/main/java/de/staropensource/engine/base/Engine.java
index b9e45eb..0ebbbb2 100644
--- a/base/src/main/java/de/staropensource/engine/base/Engine.java
+++ b/base/src/main/java/de/staropensource/engine/base/Engine.java
@@ -32,6 +32,7 @@ import de.staropensource.engine.base.internal.type.DependencySubsystemVector;
import de.staropensource.engine.base.logging.PrintStreamService;
import de.staropensource.engine.base.logging.*;
import de.staropensource.engine.base.logging.backend.async.LoggingQueue;
+import de.staropensource.engine.base.logging.backend.async.LoggingThread;
import de.staropensource.engine.base.type.DependencyVector;
import de.staropensource.engine.base.type.EngineState;
import de.staropensource.engine.base.type.immutable.ImmutableLinkedList;
@@ -51,7 +52,6 @@ import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
-import java.io.IOException;
import java.util.*;
/**
@@ -177,49 +177,104 @@ public final class Engine extends SubsystemClass {
/**
* Initializes the StarOpenSource Engine.
*
- * @throws IllegalStateException when running in an incompatible environment
- * @since v1-alpha6
+ * @throws RuntimeException for all exceptions thrown by this constructor
+ * @since v1-alpha8
*/
private Engine() throws RuntimeException {
- instance = this;
- state = EngineState.EARLY_STARTUP;
+ try {
+ instance = this;
+ state = EngineState.EARLY_STARTUP;
- long initTime = Miscellaneous.measureExecutionTime(() -> {
- try {
- de.staropensource.engine.base.logging.backend.async.LoggingThread.startThread(false);
+ // For measuring the initialization time
+ long initTimeEarly = System.currentTimeMillis();
+ long initTimeLate = initTimeEarly;
- new EngineConfiguration();
- EngineConfiguration.getInstance().loadConfiguration();
+ // Check for incompatible JVM implementations
+ checkJvmIncompatibilities();
- Logger.info("Initializing engine");
- initializeClasses(); // Initialize classes
- if (checkEnvironment()) // Check environment
- throw new IllegalStateException("Running in an incompatible environment");
- ensureEnvironment(); // Prepare the environment and ensure safety
- cacheEvents(); // Cache event listeners
+ // Display that the engine is initializing
+ Logger.verb("Initializing engine");
- Logger.verb("Completing early initialization stage");
- state = EngineState.STARTUP;
+ // Start the logging thread
+ Logger.diag("Starting logging infrastructure");
+ LoggingThread.startThread(false);
+ PrintStreamService.initializeStreams();
- // Perform automatic subsystem initialization
- if (EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) {
- collectSubsystems(); // Collect subsystems
+ // Initialize EngineInternals
+ Logger.diag("Initializing EngineInternals class");
+ new EngineInternals();
- // Initialize subsystems
- try {
- initializeSubsystems();
- } catch (Exception exception) {
- Logger.error("Subsystem dependency resolution failed");
- }
+ // Load engine configuration
+ Logger.diag("Loading engine configuration");
+ new EngineConfiguration();
+ EngineConfiguration.getInstance().loadConfiguration();
+
+ // Load engine build information
+ Logger.diag("Loading engine build information");
+ EngineInformation.update();
+
+ // Check for reflective classpath scanning compatibility
+ checkReflectiveClasspathScanningCompatibility();
+
+ // Check for Java version incompatibilities
+ checkJavaVersion();
+
+ // Initialize PlaceholderEngine
+ Logger.diag("Initializing PlaceholderEngine");
+ PlaceholderEngine.initialize();
+
+ // Initialize static FileAccess instances
+ Logger.diag("Initializing static FileAccess instances");
+ FileAccess.initializeInstances();
+
+ // Install the safety shutdown hook
+ Logger.diag("Installing safety shutdown hook");
+ EngineInternals.getInstance().installSafetyShutdownHook(true);
+
+ // Cache events
+ Logger.diag("Caching event listeners");
+ cacheEvents();
+
+ // Complete early initialization stage
+ Logger.verb("Completing early initialization stage");
+ state = EngineState.STARTUP;
+ initTimeEarly = System.currentTimeMillis() - initTimeEarly;
+
+ // Perform automatic subsystem initialization
+ if (EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) {
+ // Collect all subsystems
+ Logger.diag("Collecting subsystems");
+ collectSubsystems();
+
+ // Initialize subsystems
+ try {
+ initializeSubsystems();
+ } catch (Exception exception) {
+ Logger.error("Subsystem dependency resolution failed");
}
- } catch (Exception exception) {
- throw new RuntimeException(exception);
}
- });
- Logger.verb("Completing late initialization stage");
- state = EngineState.RUNNING;
- Logger.info("Initialized sos!engine %engine_version% (commit %engine_git_commit_id_long%-%engine_git_branch%, dirty %engine_git_dirty%) in " + initTime + "ms\nThe StarOpenSource Engine is licensed under the GNU AGPL v3. Copyright (c) 2024 The StarOpenSource Engine Authors.");
+ // Complete late initialization stage
+ Logger.verb("Completing late initialization stage");
+ state = EngineState.RUNNING;
+ initTimeLate = System.currentTimeMillis() - initTimeLate;
+
+ // Print welcome message
+ Logger.info(
+ """
+ Welcome to the StarOpenSource Engine "%engine_version_codename%" %engine_version%!
+ Running commit %engine_git_commit_id_long% (dirty %engine_git_dirty%).
+ Initialization took %init_time_total%ms (early %init_time_early%ms, late %init_time_late%ms).
+
+ Copyright (c) 2024 The StarOpenSource Engine Authors
+ Licensed under the GNU Affero General Public License v3"""
+ .replace("%init_time_total%", String.valueOf(initTimeEarly + initTimeLate))
+ .replace("%init_time_early%", String.valueOf(initTimeEarly))
+ .replace("%init_time_late%", String.valueOf(initTimeLate))
+ );
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
}
/**
@@ -230,54 +285,32 @@ public final class Engine extends SubsystemClass {
* @throws RuntimeException on engine initialization failure
* @since v1-alpha6
*/
- public static void initialize() throws IllegalStateException, RuntimeException {
+ public static void initialize() throws RuntimeException {
try {
if (instance == null)
new Engine();
} catch (RuntimeException exception) {
- if (exception.getCause() instanceof IllegalStateException)
- throw (IllegalStateException) exception.getCause();
- else {
- Logger.error("Engine initialization failed");
- Logger.error(Miscellaneous.getStackTraceHeader(exception.getCause()));
- for (String line : Miscellaneous.getStackTraceAsString(exception.getCause(), true).split("\n"))
- Logger.error(line);
- throw new RuntimeException("Engine initialization failed", exception.getCause());
- }
+ Logger.error("Engine initialization failed");
+ Logger.error(Miscellaneous.getStackTraceHeader(exception.getCause()));
+ for (String line : Miscellaneous.getStackTraceAsString(exception.getCause(), true).split("\n"))
+ Logger.error(line);
+
+ throw new RuntimeException("Engine initialization failed", exception.getCause());
}
}
/**
- * Initializes all classes.
+ * Checks if the running JVM implementation is not supported by the engine.
*
- * @since v1-alpha0
+ * @since v1-alpha8
*/
- private void initializeClasses() throws IOException {
- Logger.verb("Initializing engine classes");
+ private void checkJvmIncompatibilities() {
+ if (System.getProperties().getProperty("sosengine.base.allowUnsupportedJVMInitialization", "false").equals("true")) {
+ Logger.warn("Skipping JVM implementation incompatibilities check");
+ return;
+ }
- // Initialize essential engine classes
- new EngineInternals();
- EngineInformation.update();
- PlaceholderEngine.initialize();
-
- // Non-essential engine classes
- PrintStreamService.initializeStreams();
- FileAccess.initializeInstances();
- }
-
- /**
- * Checks if the environment is compatible with the engine build.
- *
- * @since v1-alpha4
- */
- private boolean checkEnvironment() {
- Logger.diag("Checking environment");
-
- // Warn about potential Java incompatibilities
- if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
- Logger.warn("The StarOpenSource Engine is running on an untested Java version.\nThings may not work as expected or features which can improve performance, stability, compatibility or ease of use may be missing.\nIf you encounter issues, try running a JVM implementing Java " + EngineInformation.getJavaSource());
-
- // Shutdown if running in an unsupported JVM
+ // Substrate VM (GraalVM Community)
if (JvmInformation.getImplementationName().equals("Substrate VM") && JvmInformation.getImplementationVendor().equals("GraalVM Community")) {
Logger.error("##############################################################################################");
Logger.error("## Running in Substrate VM, which is the name of the JVM used by GraalVM native-image. ##");
@@ -293,37 +326,33 @@ public final class Engine extends SubsystemClass {
Logger.error("##############################################################################################");
Runtime.getRuntime().exit(255);
}
+ }
+ /**
+ * Checks if reflective classpath scanning is supported.
+ *
+ * @since v1-alpha8
+ */
+ private void checkReflectiveClasspathScanningCompatibility() {
// Check if reflective classpath scanning is supported
- if (checkClasspathScanningSupport()) {
- Logger.warn("Running in an classpath scanning-unfriendly environment, disabling.");
+ if (System.getProperties().getProperty("sosengine.base.considerEnvironmentUnfriendlyToClasspathScanning", "false").equals("true")) {
+ Logger.warn("Running in an classpath scanning-unfriendly environment, disabling classpath scanning support.");
Logger.warn("If shit doesn't work and is expected to be discovered by annotations, you'll need to");
- Logger.warn("either register it first or have to place classes in some package.");
+ Logger.warn("either register it first or have to update some engine configuration setting.");
Logger.warn("Please consult sos!engine's documentation for more information about this issue.");
EngineInternals.getInstance().overrideReflectiveClasspathScanning(false);
}
-
- return false;
}
/**
- * Returns whether scanning the classpath is supported.
+ * Checks and warns if the Java version of the
+ * running JVM is higher than the engine supports.
*
- * @return test results
- * @since v1-alpha5
+ * @since v1-alpha8
*/
- private boolean checkClasspathScanningSupport() {
- // This may be expanded in the future
- return EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning();
- }
-
- /**
- * Ensures the execution safety of the environment.
- *
- * @since v1-alpha4
- */
- private void ensureEnvironment() {
- EngineInternals.getInstance().installSafetyShutdownHook(true);
+ private void checkJavaVersion() {
+ if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
+ Logger.warn("The StarOpenSource Engine is running on an untested Java version.\nThings may not work as expected or features which can improve performance, stability, compatibility or ease of use may be missing.\nIf you encounter issues, try running a JVM implementing Java " + EngineInformation.getJavaSource());
}
/**
@@ -332,15 +361,10 @@ public final class Engine extends SubsystemClass {
* @since v1-alpha0
*/
private void cacheEvents() {
- Logger.diag("Caching events");
-
- // Internal events
- EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
-
- // General events
EventHelper.cacheEvent(EngineCrashEvent.class);
EventHelper.cacheEvent(EngineShutdownEvent.class);
EventHelper.cacheEvent(EngineSoftCrashEvent.class);
+ EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
EventHelper.cacheEvent(LogEvent.class);
EventHelper.cacheEvent(ThrowableCatchEvent.class);
}
@@ -427,7 +451,7 @@ public final class Engine extends SubsystemClass {
resolver.addVectors(subsystems);
// Resolve dependencies and get order
- Logger.verb("Resolving subsystem dependencies");
+ Logger.diag("Resolving subsystem dependencies");
try {
for (DependencyVector vector : resolver.resolve().getOrder()) // smol workaround
order.add((DependencySubsystemVector) vector);
@@ -450,7 +474,7 @@ public final class Engine extends SubsystemClass {
}
// Initialize subsystems
- Logger.verb("Initializing engine subsystems");
+ Logger.diag("Initializing engine subsystems");
long initTime;
for (DependencySubsystemVector vector : subsystems) {
Logger.diag("Initializing subsystem '" + vector.getSubsystemClass().getName() + "' (" + vector.getSubsystemClass().getClass().getName() + ")");
diff --git a/base/src/main/java/de/staropensource/engine/base/EngineConfiguration.java b/base/src/main/java/de/staropensource/engine/base/EngineConfiguration.java
index e621f15..32eb73e 100644
--- a/base/src/main/java/de/staropensource/engine/base/EngineConfiguration.java
+++ b/base/src/main/java/de/staropensource/engine/base/EngineConfiguration.java
@@ -128,21 +128,6 @@ public final class EngineConfiguration extends Configuration {
*/
private boolean initialPerformSubsystemInitialization;
- /**
- * If enabled, will force-disable reflective classpath scanning.
- *
- * @see EngineInternals#getReflectiveClasspathScanning()
- * @see EngineInternals#overrideReflectiveClasspathScanning(boolean)
- * @since v1-alpha5
- * -- GETTER --
- * Gets the value for {@link #initialForceDisableClasspathScanning}.
- *
- * @return variable value
- * @see #initialForceDisableClasspathScanning
- * @since v1-alpha5
- */
- private boolean initialForceDisableClasspathScanning;
-
/**
* Will try to load the specified classes as subsystems,
* if reflective classpath scanning is disabled.
@@ -352,7 +337,6 @@ public final class EngineConfiguration extends Configuration {
case "debugEvents" -> debugEvents = parser.getBoolean(group + property);
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization = parser.getBoolean(group + property);
- case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning = parser.getBoolean(group + property);
case "initialIncludeSubsystemClasses" -> {
initialIncludeSubsystemClasses = new HashSet<>();
initialIncludeSubsystemClasses.addAll(Arrays.stream(parser.getString(group + property).split(",")).toList());
@@ -374,7 +358,7 @@ public final class EngineConfiguration extends Configuration {
try {
logLevel = LogLevel.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) {
- Logger.error("The log level " + parser.getString(group + property) + " is not valid");
+ Logger.error("The log level '" + parser.getString(group + property) + "' is not valid");
}
}
case "logFeatures" -> logFeatures = Set.copyOf(Arrays.stream(parser.getString(group + property).split(",")).toList());
@@ -403,7 +387,6 @@ public final class EngineConfiguration extends Configuration {
debugEvents = false;
initialPerformSubsystemInitialization = true;
- initialForceDisableClasspathScanning = false;
initialIncludeSubsystemClasses = new HashSet<>();
errorShortcodeParser = true;
@@ -428,7 +411,6 @@ public final class EngineConfiguration extends Configuration {
case "debugEvents" -> debugEvents;
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization;
- case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning;
case "initialIncludeSubsystemClasses" -> initialIncludeSubsystemClasses;
case "errorShortcodeParser" -> errorShortcodeParser;
diff --git a/base/src/main/java/de/staropensource/engine/base/internal/implementation/placeholder/EngineVersionCodename.java b/base/src/main/java/de/staropensource/engine/base/internal/implementation/placeholder/EngineVersionCodename.java
new file mode 100644
index 0000000..933fb43
--- /dev/null
+++ b/base/src/main/java/de/staropensource/engine/base/internal/implementation/placeholder/EngineVersionCodename.java
@@ -0,0 +1,46 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * Only works during early engine startup. * - * @since v1-alpha4 + * @since v1-alpha8 */ public static void initializeStreams() { - // Close all existing streams - if (diag != null) diag.close(); - if (verb != null) verb.close(); - if (sarn != null) sarn.close(); - if (info != null) info.close(); - if (warn != null) warn.close(); - if (error != null) error.close(); - if (crash != null) crash.close(); + if (Engine.getInstance() == null || Engine.getInstance().getState() != EngineState.EARLY_STARTUP) + return; // Create streams diag = LogStream.createPrintStream(LogLevel.DIAGNOSTIC); diff --git a/base/src/main/java/de/staropensource/engine/base/logging/backend/CrashHandler.java b/base/src/main/java/de/staropensource/engine/base/logging/backend/CrashHandler.java index 4ef5989..4a04f57 100644 --- a/base/src/main/java/de/staropensource/engine/base/logging/backend/CrashHandler.java +++ b/base/src/main/java/de/staropensource/engine/base/logging/backend/CrashHandler.java @@ -222,10 +222,6 @@ public final class CrashHandler { .append(EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) .append("'\n") - .append("EngineConfiguration#initialForceDisableClasspathScanning='") - .append(EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning()) - .append("'\n") - .append("EngineConfiguration#initialIncludeSubsystemClasses='") .append(EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses()) .append("'\n") diff --git a/base/src/main/java/de/staropensource/engine/base/logging/backend/Processor.java b/base/src/main/java/de/staropensource/engine/base/logging/backend/Processor.java index cde7824..0443c04 100644 --- a/base/src/main/java/de/staropensource/engine/base/logging/backend/Processor.java +++ b/base/src/main/java/de/staropensource/engine/base/logging/backend/Processor.java @@ -53,7 +53,7 @@ public final class Processor { * @since v1-alpha8 */ public static boolean isFeatureEnabled(@NotNull String feature) { - return EngineConfiguration.getInstance().getLogFeatures().contains(feature); + return EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().getLogFeatures().contains(feature); } /** @@ -66,7 +66,7 @@ public final class Processor { * @since v1-alpha8 */ public static void handle(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) { - if (EngineConfiguration.getInstance().isOptimizeLogging()) + if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isOptimizeLogging()) LoggingQueue.add(level, issuer, message); else process(level, issuer, message); @@ -84,7 +84,18 @@ public final class Processor { StringBuilder output = new StringBuilder(); // Filter out - if (level.compareTo(EngineConfiguration.getInstance().getLogLevel()) < 0) + if (EngineConfiguration.getInstance() == null) { + LogLevel maxLevel = LogLevel.INFORMATIONAL; + + try { + maxLevel = LogLevel.valueOf(System.getProperties().getProperty("sosengine.base.logLevel", "informational").toUpperCase()); + } catch (IllegalArgumentException ignored) { + Logger.error("The log level '" + System.getProperties().getProperty("sosengine.base.logLevel", "informational") + "' is not valid"); + } + + if (level.compareTo(maxLevel) < 0) + return; + } else if (level.compareTo(EngineConfiguration.getInstance().getLogLevel()) < 0) return; for (String classNameDisallowed : Filterer.disallowedClasses) diff --git a/base/src/main/java/de/staropensource/engine/base/logging/backend/async/LoggingThread.java b/base/src/main/java/de/staropensource/engine/base/logging/backend/async/LoggingThread.java index 06a25f9..a170e0a 100644 --- a/base/src/main/java/de/staropensource/engine/base/logging/backend/async/LoggingThread.java +++ b/base/src/main/java/de/staropensource/engine/base/logging/backend/async/LoggingThread.java @@ -44,18 +44,25 @@ public final class LoggingThread { * @since v1-alpha8 */ private static final @NotNull Runnable threadCode = () -> { + int pollingSpeed; + while (!( Thread.currentThread().isInterrupted() || !(EngineConfiguration.getInstance() == null || EngineConfiguration.getInstance().isOptimizeLogging()) || Engine.getInstance().getState() == EngineState.SHUTDOWN || Engine.getInstance().getState() == EngineState.CRASHED )) { + if (EngineConfiguration.getInstance() == null) + pollingSpeed = 5; + else + pollingSpeed = EngineConfiguration.getInstance().getLogPollingSpeed(); + // Flush all log messages LoggingQueue.flush(); // Sleep for whatever has been configured - if (EngineConfiguration.getInstance().getLogPollingSpeed() > 0) { - long sleepDuration = System.currentTimeMillis() + EngineConfiguration.getInstance().getLogPollingSpeed(); + if (pollingSpeed > 0) { + long sleepDuration = System.currentTimeMillis() + pollingSpeed; while (System.currentTimeMillis() < sleepDuration) Thread.onSpinWait(); } diff --git a/base/src/main/java/de/staropensource/engine/base/utility/PlaceholderEngine.java b/base/src/main/java/de/staropensource/engine/base/utility/PlaceholderEngine.java index f0de0be..c6f34cd 100644 --- a/base/src/main/java/de/staropensource/engine/base/utility/PlaceholderEngine.java +++ b/base/src/main/java/de/staropensource/engine/base/utility/PlaceholderEngine.java @@ -97,6 +97,7 @@ public final class PlaceholderEngine { placeholders.add(new EngineGitDirty()); // engine_version* placeholders.add(new EngineVersion()); + placeholders.add(new EngineVersionCodename()); placeholders.add(new EngineVersionVersion()); placeholders.add(new EngineVersionType()); placeholders.add(new EngineVersionTyperelease());