Add support for disabling classpath scanning
This commit however does not implement support for Substrate VM/native-image, which I've already tested. Sad.
This commit is contained in:
parent
ebbc1778ae
commit
0fbfe8f4e3
10 changed files with 553 additions and 119 deletions
|
@ -20,15 +20,13 @@
|
||||||
package de.staropensource.sosengine.base;
|
package de.staropensource.sosengine.base;
|
||||||
|
|
||||||
import de.staropensource.sosengine.base.annotation.EngineSubsystem;
|
import de.staropensource.sosengine.base.annotation.EngineSubsystem;
|
||||||
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
|
||||||
import de.staropensource.sosengine.base.implementable.SubsystemClass;
|
|
||||||
import de.staropensource.sosengine.base.implementable.helper.EventHelper;
|
|
||||||
import de.staropensource.sosengine.base.utility.information.EngineInformation;
|
|
||||||
import de.staropensource.sosengine.base.utility.information.JvmInformation;
|
|
||||||
import de.staropensource.sosengine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
|
||||||
import de.staropensource.sosengine.base.event.*;
|
import de.staropensource.sosengine.base.event.*;
|
||||||
import de.staropensource.sosengine.base.exception.IllegalAccessException;
|
import de.staropensource.sosengine.base.exception.IllegalAccessException;
|
||||||
import de.staropensource.sosengine.base.exception.dependency.UnmetDependenciesException;
|
import de.staropensource.sosengine.base.exception.dependency.UnmetDependenciesException;
|
||||||
|
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
||||||
|
import de.staropensource.sosengine.base.implementable.SubsystemClass;
|
||||||
|
import de.staropensource.sosengine.base.implementable.helper.EventHelper;
|
||||||
|
import de.staropensource.sosengine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
||||||
import de.staropensource.sosengine.base.internal.event.InternalEngineShutdownEvent;
|
import de.staropensource.sosengine.base.internal.event.InternalEngineShutdownEvent;
|
||||||
import de.staropensource.sosengine.base.internal.type.DependencySubsystemVector;
|
import de.staropensource.sosengine.base.internal.type.DependencySubsystemVector;
|
||||||
import de.staropensource.sosengine.base.logging.*;
|
import de.staropensource.sosengine.base.logging.*;
|
||||||
|
@ -38,6 +36,8 @@ import de.staropensource.sosengine.base.type.immutable.ImmutableLinkedList;
|
||||||
import de.staropensource.sosengine.base.utility.DependencyResolver;
|
import de.staropensource.sosengine.base.utility.DependencyResolver;
|
||||||
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
||||||
import de.staropensource.sosengine.base.utility.PlaceholderEngine;
|
import de.staropensource.sosengine.base.utility.PlaceholderEngine;
|
||||||
|
import de.staropensource.sosengine.base.utility.information.EngineInformation;
|
||||||
|
import de.staropensource.sosengine.base.utility.information.JvmInformation;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -249,12 +249,34 @@ public final class Engine extends SubsystemClass {
|
||||||
private boolean checkEnvironment() {
|
private boolean checkEnvironment() {
|
||||||
logger.diag("Checking environment");
|
logger.diag("Checking environment");
|
||||||
|
|
||||||
|
// Warn about potential Java incompatibilities
|
||||||
if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
|
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());
|
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());
|
||||||
|
|
||||||
|
|
||||||
|
// Check if reflective classpath scanning is supported
|
||||||
|
if (checkClasspathScanningSupport()) {
|
||||||
|
logger.warn("Running in an classpath scanning-unfriendly environment, disabling.");
|
||||||
|
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("Please consult sos!engine's documentation for more information about this issue.");
|
||||||
|
EngineInternals.getInstance().overrideReflectiveClasspathScanning(false);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether scanning the classpath is supported.
|
||||||
|
*
|
||||||
|
* @return test results
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
private boolean checkClasspathScanningSupport() {
|
||||||
|
// This may be expanded in the future
|
||||||
|
return EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures the execution safety of the environment.
|
* Ensures the execution safety of the environment.
|
||||||
*
|
*
|
||||||
|
@ -354,21 +376,11 @@ public final class Engine extends SubsystemClass {
|
||||||
*/
|
*/
|
||||||
private void collectSubsystems() {
|
private void collectSubsystems() {
|
||||||
ArrayList<@NotNull DependencySubsystemVector> subsystemsMutable = new ArrayList<>();
|
ArrayList<@NotNull DependencySubsystemVector> subsystemsMutable = new ArrayList<>();
|
||||||
|
|
||||||
// Scan entire classpath using the Reflections library
|
|
||||||
Reflections reflections = new Reflections(
|
|
||||||
new ConfigurationBuilder()
|
|
||||||
.setUrls(ClasspathHelper.forJavaClassPath())
|
|
||||||
.setScanners(Scanners.TypesAnnotated)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get annotated methods
|
|
||||||
Set<@NotNull Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(EngineSubsystem.class);
|
|
||||||
|
|
||||||
// Initialize classes, get dependency vector and add to 'subsystemsMutable'
|
|
||||||
Object initializedClassRaw;
|
Object initializedClassRaw;
|
||||||
SubsystemClass initializedClass;
|
SubsystemClass initializedClass;
|
||||||
for (Class<?> clazz : annotatedClasses) {
|
|
||||||
|
// Check and initialize all classes, get dependency vector and check version, then add to 'subsystemsMutable'
|
||||||
|
for (Class<?> clazz : getRawSubsystemClasses())
|
||||||
try {
|
try {
|
||||||
// Create new instance
|
// Create new instance
|
||||||
initializedClassRaw = clazz.getDeclaredConstructor().newInstance();
|
initializedClassRaw = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
@ -387,12 +399,43 @@ public final class Engine extends SubsystemClass {
|
||||||
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Invalid version string: " + exception.getMessage().replace("The version string is invalid: ", ""));
|
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Invalid version string: " + exception.getMessage().replace("The version string is invalid: ", ""));
|
||||||
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Method invocation error", exception);
|
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Method invocation error", exception);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update 'subsystems'
|
// Update 'subsystems'
|
||||||
subsystems = new ImmutableLinkedList<>(subsystemsMutable);
|
subsystems = new ImmutableLinkedList<>(subsystemsMutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of classes which are potentially
|
||||||
|
* eligible for subsystem initialization.
|
||||||
|
*
|
||||||
|
* @return potential subsystem classes
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
private Set<@NotNull Class<?>> getRawSubsystemClasses() {
|
||||||
|
Set<@NotNull Class<?>> classes = new HashSet<>();
|
||||||
|
|
||||||
|
if (EngineInternals.getInstance().getReflectiveClasspathScanning()) {
|
||||||
|
// Scan entire classpath using the Reflections library
|
||||||
|
Reflections reflections = new Reflections(
|
||||||
|
new ConfigurationBuilder()
|
||||||
|
.setUrls(ClasspathHelper.forJavaClassPath())
|
||||||
|
.setScanners(Scanners.TypesAnnotated)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get annotated methods
|
||||||
|
classes = reflections.getTypesAnnotatedWith(EngineSubsystem.class);
|
||||||
|
} else
|
||||||
|
for (String path : EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses())
|
||||||
|
try {
|
||||||
|
logger.diag("Resolving class " + path);
|
||||||
|
classes.add(Class.forName(path));
|
||||||
|
} catch (ClassNotFoundException exception) {
|
||||||
|
logger.error("Failed loading subsystem class " + path + ": Class not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all subsystems.
|
* Initializes all subsystems.
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,7 +32,10 @@ import lombok.Getter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the base engine configuration.
|
* Provides the base engine configuration.
|
||||||
|
@ -119,6 +122,35 @@ public final class EngineConfiguration extends Configuration {
|
||||||
*/
|
*/
|
||||||
private boolean debugShortcodeConverter;
|
private boolean debugShortcodeConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
* -- GETTER --
|
||||||
|
* Gets the value for {@link #initialIncludeSubsystemClasses}.
|
||||||
|
*
|
||||||
|
* @return variable value
|
||||||
|
* @see #initialIncludeSubsystemClasses
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
private Set<@NotNull String> initialIncludeSubsystemClasses;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If enabled, invalid shortcodes will be logged by the {@link ShortcodeParser}.
|
* If enabled, invalid shortcodes will be logged by the {@link ShortcodeParser}.
|
||||||
* The message will be printed as a {@link LogLevel#SILENT_WARNING}.
|
* The message will be printed as a {@link LogLevel#SILENT_WARNING}.
|
||||||
|
@ -322,6 +354,12 @@ public final class EngineConfiguration extends Configuration {
|
||||||
case "debugEvents" -> debugEvents = parser.getBoolean(group + property);
|
case "debugEvents" -> debugEvents = parser.getBoolean(group + property);
|
||||||
case "debugShortcodeConverter" -> debugShortcodeConverter = parser.getBoolean(group + property);
|
case "debugShortcodeConverter" -> debugShortcodeConverter = 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());
|
||||||
|
}
|
||||||
|
|
||||||
case "errorShortcodeConverter" -> errorShortcodeConverter = parser.getBoolean(group + property);
|
case "errorShortcodeConverter" -> errorShortcodeConverter = parser.getBoolean(group + property);
|
||||||
|
|
||||||
case "optimizeLogging" -> {
|
case "optimizeLogging" -> {
|
||||||
|
@ -369,6 +407,9 @@ public final class EngineConfiguration extends Configuration {
|
||||||
debugEvents = false;
|
debugEvents = false;
|
||||||
debugShortcodeConverter = false;
|
debugShortcodeConverter = false;
|
||||||
|
|
||||||
|
initialForceDisableClasspathScanning = false;
|
||||||
|
initialIncludeSubsystemClasses = new HashSet<>();
|
||||||
|
|
||||||
errorShortcodeConverter = true;
|
errorShortcodeConverter = true;
|
||||||
|
|
||||||
optimizeLogging = true;
|
optimizeLogging = true;
|
||||||
|
@ -393,6 +434,9 @@ public final class EngineConfiguration extends Configuration {
|
||||||
case "debugEvents" -> debugEvents;
|
case "debugEvents" -> debugEvents;
|
||||||
case "debugShortcodeConverter" -> debugShortcodeConverter;
|
case "debugShortcodeConverter" -> debugShortcodeConverter;
|
||||||
|
|
||||||
|
case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning;
|
||||||
|
case "initialIncludeSubsystemClasses" -> initialIncludeSubsystemClasses;
|
||||||
|
|
||||||
case "errorShortcodeConverter" -> errorShortcodeConverter;
|
case "errorShortcodeConverter" -> errorShortcodeConverter;
|
||||||
|
|
||||||
case "optimizeLogging" -> optimizeLogging;
|
case "optimizeLogging" -> optimizeLogging;
|
||||||
|
|
|
@ -19,9 +19,12 @@
|
||||||
|
|
||||||
package de.staropensource.sosengine.base;
|
package de.staropensource.sosengine.base;
|
||||||
|
|
||||||
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
|
||||||
import de.staropensource.sosengine.base.exception.IllegalAccessException;
|
import de.staropensource.sosengine.base.exception.IllegalAccessException;
|
||||||
|
import de.staropensource.sosengine.base.implementable.EventListenerCode;
|
||||||
|
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
||||||
|
import de.staropensource.sosengine.base.implementable.helper.EventHelper;
|
||||||
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
||||||
|
import de.staropensource.sosengine.base.type.EventPriority;
|
||||||
import de.staropensource.sosengine.base.type.InternalAccessArea;
|
import de.staropensource.sosengine.base.type.InternalAccessArea;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -72,6 +75,21 @@ public final class EngineInternals {
|
||||||
@Getter
|
@Getter
|
||||||
private final @NotNull List<@NotNull InternalAccessArea> restrictedAreas = new ArrayList<>();
|
private final @NotNull List<@NotNull InternalAccessArea> restrictedAreas = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains whether the engine should reflectively
|
||||||
|
* search the classpath for events or other annotations.
|
||||||
|
* <p>
|
||||||
|
* If disabled, code will either have to manually call
|
||||||
|
* registration methods or certain classes have to
|
||||||
|
* be created in a certain package, depending on the
|
||||||
|
* use case and application.
|
||||||
|
*
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode)
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
private boolean reflectiveClasspathScanning = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this class.
|
* Constructs this class.
|
||||||
*
|
*
|
||||||
|
@ -110,10 +128,12 @@ public final class EngineInternals {
|
||||||
areas.remove(InternalAccessArea.ALL);
|
areas.remove(InternalAccessArea.ALL);
|
||||||
areas.remove(InternalAccessArea.ALL_READ);
|
areas.remove(InternalAccessArea.ALL_READ);
|
||||||
areas.remove(InternalAccessArea.ALL_WRITE);
|
areas.remove(InternalAccessArea.ALL_WRITE);
|
||||||
|
areas.remove(InternalAccessArea.ALL_READ_ESSENTIAL);
|
||||||
restrictedAreas.addAll(areas);
|
restrictedAreas.addAll(areas);
|
||||||
}
|
}
|
||||||
case ALL_READ -> restrictedAreas.addAll(Arrays.stream(InternalAccessArea.valuesReadOnly()).toList());
|
|
||||||
case ALL_WRITE -> restrictedAreas.addAll(Arrays.stream(InternalAccessArea.valuesWriteOnly()).toList());
|
case ALL_WRITE -> restrictedAreas.addAll(Arrays.stream(InternalAccessArea.valuesWriteOnly()).toList());
|
||||||
|
case ALL_READ -> restrictedAreas.addAll(Arrays.stream(InternalAccessArea.valuesReadOnly()).toList());
|
||||||
|
case ALL_READ_ESSENTIAL -> restrictedAreas.addAll(Arrays.stream(InternalAccessArea.valuesEssentialReadOnly()).toList());
|
||||||
default -> restrictedAreas.add(area);
|
default -> restrictedAreas.add(area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,11 +145,11 @@ public final class EngineInternals {
|
||||||
* Highly recommended to keep enabled.
|
* Highly recommended to keep enabled.
|
||||||
*
|
*
|
||||||
* @param status {@code true} to install, {@code false} otherwise
|
* @param status {@code true} to install, {@code false} otherwise
|
||||||
* @throws IllegalAccessException when restricted
|
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#SAFETY_SHUTDOWN_HOOK_UPDATE})
|
||||||
* @since v1-alpha4
|
* @since v1-alpha4
|
||||||
*/
|
*/
|
||||||
public void installSafetyShutdownHook(boolean status) throws IllegalAccessException {
|
public void installSafetyShutdownHook(boolean status) throws IllegalAccessException {
|
||||||
isRestricted(InternalAccessArea.SAFETY_SHUTDOWN_HOOK);
|
isRestricted(InternalAccessArea.SAFETY_SHUTDOWN_HOOK_UPDATE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (status)
|
if (status)
|
||||||
|
@ -139,13 +159,27 @@ public final class EngineInternals {
|
||||||
} catch (IllegalArgumentException | IllegalStateException ignored) {}
|
} catch (IllegalArgumentException | IllegalStateException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the engine's shutdown handler.
|
||||||
|
* The shutdown handler is responsible for
|
||||||
|
* shutting down the JVM safely.
|
||||||
|
*
|
||||||
|
* @return shutdown handler
|
||||||
|
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#SHUTDOWN_HANDLER_GET})
|
||||||
|
* @since v1-alpha4
|
||||||
|
*/
|
||||||
|
public @NotNull ShutdownHandler getShutdownHandler() throws IllegalAccessException {
|
||||||
|
isRestricted(InternalAccessArea.SHUTDOWN_HANDLER_GET);
|
||||||
|
return Engine.getInstance().getShutdownHandler();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the engine's shutdown handler.
|
* Sets the engine's shutdown handler.
|
||||||
* The shutdown handler is responsible for
|
* The shutdown handler is responsible for
|
||||||
* shutting down the JVM safely.
|
* shutting down the JVM safely.
|
||||||
*
|
*
|
||||||
* @param shutdownHandler new shutdown handler
|
* @param shutdownHandler new shutdown handler
|
||||||
* @throws IllegalAccessException when restricted
|
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#SHUTDOWN_HANDLER_UPDATE})
|
||||||
* @since v1-alpha4
|
* @since v1-alpha4
|
||||||
*/
|
*/
|
||||||
public void setShutdownHandler(@NotNull ShutdownHandler shutdownHandler) throws IllegalAccessException {
|
public void setShutdownHandler(@NotNull ShutdownHandler shutdownHandler) throws IllegalAccessException {
|
||||||
|
@ -154,16 +188,47 @@ public final class EngineInternals {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the engine's shutdown handler.
|
* Returns whether the engine should reflectively
|
||||||
* The shutdown handler is responsible for
|
* search the classpath for events or other annotations.
|
||||||
* shutting down the JVM safely.
|
* <p>
|
||||||
|
* If disabled, code will either have to manually call
|
||||||
|
* registration methods or certain classes have to
|
||||||
|
* be created in a certain package, depending on the
|
||||||
|
* use case and application.
|
||||||
*
|
*
|
||||||
* @return shutdown handler
|
* @return reflective classpath scanning flag state
|
||||||
* @throws IllegalAccessException when restricted
|
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#REFLECTIVE_CLASSPATH_SCANNING_GET})
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode)
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
|
||||||
* @since v1-alpha4
|
* @since v1-alpha4
|
||||||
*/
|
*/
|
||||||
public @NotNull ShutdownHandler getShutdownHandler() throws IllegalAccessException {
|
public boolean getReflectiveClasspathScanning() throws IllegalAccessException {
|
||||||
isRestricted(InternalAccessArea.SHUTDOWN_HANDLER_GET);
|
isRestricted(InternalAccessArea.REFLECTIVE_CLASSPATH_SCANNING_GET);
|
||||||
return Engine.getInstance().getShutdownHandler();
|
return reflectiveClasspathScanning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides whether the engine should reflectively
|
||||||
|
* search the classpath for events or other annotations.
|
||||||
|
* <p>
|
||||||
|
* If disabled, code will either have to manually call
|
||||||
|
* registration methods or certain classes have to
|
||||||
|
* be created in a certain package, depending on the
|
||||||
|
* use case and application.
|
||||||
|
* <p>
|
||||||
|
* Enabling reflective classpath scanning in an unsupported
|
||||||
|
* environment may cause minor to extreme side effects,
|
||||||
|
* including but not limited to <b>bugs, exceptions, engine
|
||||||
|
* or even whole JVM crashes</b>. <i>You have been warned!</i>
|
||||||
|
*
|
||||||
|
* @param reflectiveClasspathScanning new reflective classpath scanning
|
||||||
|
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#REFLECTIVE_CLASSPATH_SCANNING_OVERRIDE})
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode)
|
||||||
|
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
public void overrideReflectiveClasspathScanning(boolean reflectiveClasspathScanning) throws IllegalAccessException {
|
||||||
|
isRestricted(InternalAccessArea.REFLECTIVE_CLASSPATH_SCANNING_OVERRIDE);
|
||||||
|
this.reflectiveClasspathScanning = reflectiveClasspathScanning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.sosengine.base.implementable;
|
||||||
|
|
||||||
|
import de.staropensource.sosengine.base.implementable.helper.EventHelper;
|
||||||
|
import de.staropensource.sosengine.base.type.EventPriority;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@link EventHelper} to execute event listeners.
|
||||||
|
*
|
||||||
|
* @see Runnable
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public abstract class EventListenerCode {
|
||||||
|
/**
|
||||||
|
* Contains the priority of this
|
||||||
|
* event listener.
|
||||||
|
* <p>
|
||||||
|
* Set automatically by {@link EventHelper},
|
||||||
|
* do not change this manually.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public @NotNull EventPriority priority = EventPriority.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the event listener.
|
||||||
|
*
|
||||||
|
* @param arguments arguments passed along by the event
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public abstract void run(Object... arguments) throws Exception;
|
||||||
|
}
|
|
@ -20,6 +20,7 @@
|
||||||
package de.staropensource.sosengine.base.implementable.helper;
|
package de.staropensource.sosengine.base.implementable.helper;
|
||||||
|
|
||||||
import de.staropensource.sosengine.base.EngineConfiguration;
|
import de.staropensource.sosengine.base.EngineConfiguration;
|
||||||
|
import de.staropensource.sosengine.base.EngineInternals;
|
||||||
import de.staropensource.sosengine.base.annotation.EventListener;
|
import de.staropensource.sosengine.base.annotation.EventListener;
|
||||||
import de.staropensource.sosengine.base.event.LogEvent;
|
import de.staropensource.sosengine.base.event.LogEvent;
|
||||||
import de.staropensource.sosengine.base.exception.reflection.InstanceMethodFromStaticContextException;
|
import de.staropensource.sosengine.base.exception.reflection.InstanceMethodFromStaticContextException;
|
||||||
|
@ -27,9 +28,10 @@ import de.staropensource.sosengine.base.exception.reflection.InvalidMethodSignat
|
||||||
import de.staropensource.sosengine.base.exception.reflection.NoAccessException;
|
import de.staropensource.sosengine.base.exception.reflection.NoAccessException;
|
||||||
import de.staropensource.sosengine.base.exception.reflection.StaticInitializerException;
|
import de.staropensource.sosengine.base.exception.reflection.StaticInitializerException;
|
||||||
import de.staropensource.sosengine.base.implementable.Event;
|
import de.staropensource.sosengine.base.implementable.Event;
|
||||||
|
import de.staropensource.sosengine.base.implementable.EventListenerCode;
|
||||||
|
import de.staropensource.sosengine.base.internal.implementation.EventListenerMethod;
|
||||||
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
||||||
import de.staropensource.sosengine.base.reflection.Reflect;
|
import de.staropensource.sosengine.base.type.EventPriority;
|
||||||
import de.staropensource.sosengine.base.reflection.ReflectionMethod;
|
|
||||||
import de.staropensource.sosengine.base.utility.ListFormatter;
|
import de.staropensource.sosengine.base.utility.ListFormatter;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -41,10 +43,7 @@ import org.reflections.util.ConfigurationBuilder;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Comparator;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplifies event logging and calling.
|
* Simplifies event logging and calling.
|
||||||
|
@ -53,14 +52,6 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public final class EventHelper {
|
public final class EventHelper {
|
||||||
/**
|
|
||||||
* Holds all cached events.
|
|
||||||
* Should not be modified manually.
|
|
||||||
*
|
|
||||||
* @since v1-alpha0
|
|
||||||
*/
|
|
||||||
private static final @NotNull HashMap<@NotNull Class<? extends Event>, LinkedList<@NotNull ReflectionMethod>> cachedEventListeners = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the {@link LoggerInstance} for this instance.
|
* Contains the {@link LoggerInstance} for this instance.
|
||||||
*
|
*
|
||||||
|
@ -69,60 +60,111 @@ public final class EventHelper {
|
||||||
*/
|
*/
|
||||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(EventHelper.class).setOrigin("ENGINE").build();
|
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(EventHelper.class).setOrigin("ENGINE").build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds all cached events.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
private static final @NotNull Map<@NotNull Class<? extends Event>, LinkedList<@NotNull EventListenerCode>> cachedEventListeners = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this class.
|
* Constructs this class.
|
||||||
*
|
*
|
||||||
* @since v1-alpha0
|
* @since v1-alpha0
|
||||||
*/
|
*/
|
||||||
public EventHelper() {}
|
private EventHelper() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all {@link EventListener}s listening on some event.
|
* Registers a new {@link Event}.
|
||||||
* The classpath will be scanned for listeners, unless cached results exist and {@code !forceScanning}.
|
* <p>
|
||||||
|
* This method does nothing if classpath searching is disabled.
|
||||||
*
|
*
|
||||||
* @param event event class
|
* @param event {@link Event} to register for
|
||||||
* @param forceScanning forces scanning the classpath for listeners. not recommended due to a huge performance penalty
|
* @param eventListener {@link EventListenerCode} to register
|
||||||
* @return list of event listeners
|
* @param priority priority of the listener
|
||||||
* @see #cacheEvent(Class)
|
* @see EngineInternals#getReflectiveClasspathScanning()
|
||||||
* @since v1-alpha0
|
* @since v1-alpha5
|
||||||
*/
|
*/
|
||||||
public static @NotNull LinkedList<ReflectionMethod> getAnnotatedMethods(@NotNull Class<? extends Event> event, boolean forceScanning) {
|
public static synchronized void registerEvent(@NotNull Class<? extends Event> event, @NotNull EventListenerCode eventListener, @NotNull EventPriority priority) {
|
||||||
LinkedList<ReflectionMethod> methods = new LinkedList<>();
|
if (EngineInternals.getInstance().getReflectiveClasspathScanning())
|
||||||
|
return;
|
||||||
|
|
||||||
if (forceScanning || !cachedEventListeners.containsKey(event)) {
|
// Update 'eventListener' priority
|
||||||
Reflections reflections = new Reflections(
|
eventListener.priority = priority;
|
||||||
new ConfigurationBuilder()
|
|
||||||
.setUrls(ClasspathHelper.forJavaClassPath())
|
|
||||||
.setScanners(Scanners.MethodsAnnotated)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get annotated methods
|
// Check if event already exists in map
|
||||||
Set<@NotNull Method> annotatedMethods = reflections.getMethodsAnnotatedWith(EventListener.class);
|
// If not, create entry with a LinkedList
|
||||||
|
if (cachedEventListeners.containsKey(event))
|
||||||
|
if (cachedEventListeners.get(event).contains(eventListener))
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
cachedEventListeners.get(event).add(eventListener);
|
||||||
|
else {
|
||||||
|
LinkedList<@NotNull EventListenerCode> list = new LinkedList<>();
|
||||||
|
list.add(eventListener);
|
||||||
|
cachedEventListeners.put(event, list);
|
||||||
|
}
|
||||||
|
|
||||||
// Sort event listeners not listening for this event out
|
logger.diag("Registered event listener " + eventListener + " for event " + event + " with priority " + priority.name());
|
||||||
for (Method method : annotatedMethods)
|
|
||||||
if (method.getAnnotation(EventListener.class).event() == event)
|
|
||||||
methods.add(Reflect.reflectOn(method));
|
|
||||||
|
|
||||||
// Sort 'methods' linked list after event priority
|
|
||||||
methods.sort(Comparator.comparing(method -> method.getAnnotation(EventListener.class).priority()));
|
|
||||||
} else
|
|
||||||
// Event listeners are cached and !forceScanning, return cached results
|
|
||||||
methods = cachedEventListeners.get(event);
|
|
||||||
|
|
||||||
return methods;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all {@link EventListener}s listening on some event.
|
* Registers a new {@link Event}.
|
||||||
* The classpath will be scanned for listeners, unless cached results exist.
|
* <p>
|
||||||
|
* This method does nothing if classpath searching is disabled.
|
||||||
*
|
*
|
||||||
* @param event event class
|
* @param event {@link Event} to register for
|
||||||
* @return list of event listeners
|
* @param eventListener {@link EventListenerCode} to register
|
||||||
* @since v1-alpha0
|
* @see EngineInternals#getReflectiveClasspathScanning()
|
||||||
|
* @since v1-alpha5
|
||||||
*/
|
*/
|
||||||
public static @NotNull LinkedList<ReflectionMethod> getAnnotatedMethods(@NotNull Class<? extends Event> event) {
|
public static void registerEvent(@NotNull Class<? extends Event> event, @NotNull EventListenerCode eventListener) {
|
||||||
return getAnnotatedMethods(event, false);
|
registerEvent(event, eventListener, EventPriority.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Re-)Caches all event listeners for some {@link Event}.
|
||||||
|
* <p>
|
||||||
|
* This method does nothing if classpath searching is enabled.
|
||||||
|
*
|
||||||
|
* @param event event to (re-)cache. Set to {@code null} to recompute all cached events
|
||||||
|
* @see EngineInternals#getReflectiveClasspathScanning()
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public static synchronized void cacheEvent(@Nullable Class<? extends Event> event) {
|
||||||
|
if (!EngineInternals.getInstance().getReflectiveClasspathScanning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (event == null)
|
||||||
|
for (Class<? extends Event> cachedEvent : cachedEventListeners.keySet())
|
||||||
|
cacheEvent(cachedEvent);
|
||||||
|
else {
|
||||||
|
LinkedList<@NotNull EventListenerCode> annotatedMethods = getAnnotatedMethods(event);
|
||||||
|
|
||||||
|
if (cachedEventListeners.containsKey(event))
|
||||||
|
cachedEventListeners.replace(event, annotatedMethods);
|
||||||
|
else
|
||||||
|
cachedEventListeners.put(event, annotatedMethods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an event from the event listener cache.
|
||||||
|
* <p>
|
||||||
|
* This method does nothing if classpath searching is enabled.
|
||||||
|
*
|
||||||
|
* @param event event to uncache. Set to {@code null} to clear the entire cache
|
||||||
|
* @see EngineInternals#getReflectiveClasspathScanning()
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public static synchronized void uncacheEvent(@Nullable Class<? extends Event> event) {
|
||||||
|
if (!EngineInternals.getInstance().getReflectiveClasspathScanning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (event == null)
|
||||||
|
cachedEventListeners.clear();
|
||||||
|
else
|
||||||
|
cachedEventListeners.remove(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +172,7 @@ public final class EventHelper {
|
||||||
*
|
*
|
||||||
* @param event event class
|
* @param event event class
|
||||||
* @param arguments arguments to pass to event listeners
|
* @param arguments arguments to pass to event listeners
|
||||||
* @since v1-alpha0
|
* @since v1-alpha5
|
||||||
*/
|
*/
|
||||||
public static void invokeAnnotatedMethods(@NotNull Class<? extends Event> event, Object... arguments) {
|
public static void invokeAnnotatedMethods(@NotNull Class<? extends Event> event, Object... arguments) {
|
||||||
if (event != LogEvent.class && EngineConfiguration.getInstance().isDebugEvents())
|
if (event != LogEvent.class && EngineConfiguration.getInstance().isDebugEvents())
|
||||||
|
@ -140,21 +182,21 @@ public final class EventHelper {
|
||||||
logger.diag("Event " + event.getName() + " was emitted, passing arguments " + ListFormatter.formatArray(arguments));
|
logger.diag("Event " + event.getName() + " was emitted, passing arguments " + ListFormatter.formatArray(arguments));
|
||||||
|
|
||||||
Runnable eventCode = () -> {
|
Runnable eventCode = () -> {
|
||||||
for (ReflectionMethod method : getAnnotatedMethods(event)) {
|
for (EventListenerCode eventListener : getAnnotatedMethods(event)) {
|
||||||
try {
|
try {
|
||||||
method.invoke(arguments);
|
eventListener.run(arguments);
|
||||||
} catch (NoAccessException exception) {
|
} catch (NoAccessException exception) {
|
||||||
logger.warn("Event listener method " + method.getName() + " could not be called as the method could not be accessed");
|
logger.warn("Event listener " + eventListener + " could not be called as the method could not be accessed");
|
||||||
} catch (InvalidMethodSignatureException exception) {
|
} catch (InvalidMethodSignatureException exception) {
|
||||||
logger.warn("Event listener method " + method.getName() + " has an invalid method signature");
|
logger.warn("Event listener " + eventListener + " has an invalid method signature");
|
||||||
} catch (InvocationTargetException exception) {
|
} catch (InvocationTargetException exception) {
|
||||||
logger.crash("Event listener method " + method.getName() + " threw a throwable", exception.getTargetException(), true);
|
logger.crash("Event listener " + eventListener + " threw a throwable", exception.getTargetException(), true);
|
||||||
} catch (InstanceMethodFromStaticContextException exception) {
|
} catch (InstanceMethodFromStaticContextException exception) {
|
||||||
logger.warn("Event listener method " + method.getName() + " is not static. Event listener methods must be static for them to be called.");
|
logger.warn("Event listener " + eventListener + " is not static. Event listener methods must be static for them to be called.");
|
||||||
} catch (StaticInitializerException exception) {
|
} catch (StaticInitializerException exception) {
|
||||||
logger.crash("Event listener method " + method.getName() + " could not be called as the static initializer failed", exception.getThrowable(), true);
|
logger.crash("Event listener " + eventListener + " could not be called as the static initializer failed", exception.getThrowable(), true);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
logger.crash("Event listener method " + method.getName() + " could not be called as an error occurred during reflection", exception, true);
|
logger.crash("Event listener " + eventListener + " could not be called as an error occurred during reflection", exception, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -169,35 +211,54 @@ public final class EventHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Re-)Caches all event listeners for some {@link Event}.
|
* Returns all {@link EventListener}s listening on some event.
|
||||||
|
* The classpath will be scanned for listeners, unless cached results exist and {@code !forceScanning}.
|
||||||
*
|
*
|
||||||
* @param event event to (re-)cache. Set to {@code null} to recompute all cached events
|
* @param event event class
|
||||||
* @since v1-alpha0
|
* @param forceScanning forces scanning the classpath for listeners. not recommended due to a huge performance penalty
|
||||||
|
* @return list of event listeners
|
||||||
|
* @see #cacheEvent(Class)
|
||||||
|
* @since v1-alpha5
|
||||||
*/
|
*/
|
||||||
public static synchronized void cacheEvent(@Nullable Class<? extends Event> event) {
|
public static @NotNull LinkedList<EventListenerCode> getAnnotatedMethods(@NotNull Class<? extends Event> event, boolean forceScanning) {
|
||||||
if (event == null)
|
LinkedList<EventListenerCode> eventListeners = new LinkedList<>();
|
||||||
for (Class<? extends Event> cachedEvent : cachedEventListeners.keySet())
|
|
||||||
cacheEvent(cachedEvent);
|
|
||||||
else {
|
|
||||||
LinkedList<@NotNull ReflectionMethod> annotatedMethods = getAnnotatedMethods(event);
|
|
||||||
|
|
||||||
if (cachedEventListeners.containsKey(event))
|
if (!EngineInternals.getInstance().getReflectiveClasspathScanning())
|
||||||
cachedEventListeners.replace(event, annotatedMethods);
|
return Objects.requireNonNullElse(cachedEventListeners.get(event), eventListeners);
|
||||||
else
|
|
||||||
cachedEventListeners.put(event, annotatedMethods);
|
if (forceScanning || !cachedEventListeners.containsKey(event)) {
|
||||||
}
|
Reflections reflections = new Reflections(
|
||||||
|
new ConfigurationBuilder()
|
||||||
|
.setUrls(ClasspathHelper.forJavaClassPath())
|
||||||
|
.setScanners(Scanners.MethodsAnnotated)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get annotated methods
|
||||||
|
Set<@NotNull Method> annotatedMethods = reflections.getMethodsAnnotatedWith(EventListener.class);
|
||||||
|
|
||||||
|
// Sort event listeners not listening for the specified event out
|
||||||
|
for (Method method : annotatedMethods)
|
||||||
|
if (method.getAnnotation(EventListener.class).event() == event)
|
||||||
|
eventListeners.add(new EventListenerMethod(method));
|
||||||
|
|
||||||
|
// Sort list after event priority
|
||||||
|
eventListeners.sort(Comparator.comparing(method -> Objects.requireNonNull(((EventListenerMethod) method).getAnnotation(EventListener.class)).priority()));
|
||||||
|
} else
|
||||||
|
// Event listeners are cached and !forceScanning, return cached results
|
||||||
|
eventListeners = cachedEventListeners.get(event);
|
||||||
|
|
||||||
|
return eventListeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an event from the event listener cache.
|
* Returns all {@link EventListener}s listening on some event.
|
||||||
|
* The classpath will be scanned for listeners, unless cached results exist.
|
||||||
*
|
*
|
||||||
* @param event event to uncache. Set to {@code null} to clear the entire cache
|
* @param event event class
|
||||||
* @since v1-alpha0
|
* @return list of event listeners
|
||||||
|
* @since v1-alpha5
|
||||||
*/
|
*/
|
||||||
public static synchronized void uncacheEvent(@Nullable Class<? extends Event> event) {
|
public static @NotNull LinkedList<EventListenerCode> getAnnotatedMethods(@NotNull Class<? extends Event> event) {
|
||||||
if (event == null)
|
return getAnnotatedMethods(event, false);
|
||||||
cachedEventListeners.clear();
|
|
||||||
else
|
|
||||||
cachedEventListeners.remove(event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.sosengine.base.internal.implementation;
|
||||||
|
|
||||||
|
import de.staropensource.sosengine.base.implementable.EventListenerCode;
|
||||||
|
import de.staropensource.sosengine.base.reflection.Reflect;
|
||||||
|
import de.staropensource.sosengine.base.reflection.ReflectionMethod;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface specifically for executing event listener methods.
|
||||||
|
*
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
public final class EventListenerMethod extends EventListenerCode {
|
||||||
|
/**
|
||||||
|
* Contains the method to call and get.
|
||||||
|
*
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
private final @NotNull ReflectionMethod method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this class.
|
||||||
|
*
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
public EventListenerMethod(@NotNull Method method) {
|
||||||
|
this.method = Reflect.reflectOn(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void run(Object[] arguments) throws Exception {
|
||||||
|
method.invoke(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards {@link ReflectionMethod#getAnnotation(Class)}
|
||||||
|
* to the internal {@link ReflectionMethod} instance.
|
||||||
|
*
|
||||||
|
* @param annotation annotation to get
|
||||||
|
* @return annotation or {@code null} on error
|
||||||
|
* @see ReflectionMethod#getAnnotation(Class)
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
public <T extends Annotation> @Nullable T getAnnotation(@NotNull Class<T> annotation) {
|
||||||
|
try {
|
||||||
|
return method.getAnnotation(annotation);
|
||||||
|
} catch (NullPointerException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "method " + method.getMethod().getDeclaringClass().getName() + "#" + method.getName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interfaces and abstract classes which can be used for implementing classes.
|
||||||
|
*
|
||||||
|
* @since v1-alpha0
|
||||||
|
*/
|
||||||
|
package de.staropensource.sosengine.base.internal.implementation;
|
|
@ -20,6 +20,7 @@
|
||||||
package de.staropensource.sosengine.base.type;
|
package de.staropensource.sosengine.base.type;
|
||||||
|
|
||||||
import de.staropensource.sosengine.base.EngineInternals;
|
import de.staropensource.sosengine.base.EngineInternals;
|
||||||
|
import de.staropensource.sosengine.base.implementable.Event;
|
||||||
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
import de.staropensource.sosengine.base.implementable.ShutdownHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,13 +52,23 @@ public enum InternalAccessArea {
|
||||||
*/
|
*/
|
||||||
ALL_READ,
|
ALL_READ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refers to all essential read-only areas.
|
||||||
|
* <p>
|
||||||
|
* Essential read-only areas are IIAs which are
|
||||||
|
* very important and should not be restricted.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
ALL_READ_ESSENTIAL,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refers to the toggling of the JVM shutdown hook, which
|
* Refers to the toggling of the JVM shutdown hook, which
|
||||||
* prevents JVM shutdowns without the engine first shutting down.
|
* prevents JVM shutdowns without the engine first shutting down.
|
||||||
*
|
*
|
||||||
* @since v1-alpha4
|
* @since v1-alpha4
|
||||||
*/
|
*/
|
||||||
SAFETY_SHUTDOWN_HOOK,
|
SAFETY_SHUTDOWN_HOOK_UPDATE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refers to the getting of the engine's shutdown handler.
|
* Refers to the getting of the engine's shutdown handler.
|
||||||
|
@ -75,7 +86,23 @@ public enum InternalAccessArea {
|
||||||
*
|
*
|
||||||
* @since v1-alpha4
|
* @since v1-alpha4
|
||||||
*/
|
*/
|
||||||
SHUTDOWN_HANDLER_UPDATE;
|
SHUTDOWN_HANDLER_UPDATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refers to the getting of the flag controlling whether
|
||||||
|
* automatic {@link Event} classpath searching should be performed.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
REFLECTIVE_CLASSPATH_SCANNING_GET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refers to the overriding of the flag controlling whether
|
||||||
|
* automatic {@link Event} classpath searching should be performed.
|
||||||
|
*
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
REFLECTIVE_CLASSPATH_SCANNING_OVERRIDE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all read-only areas.
|
* Returns all read-only areas.
|
||||||
|
@ -89,6 +116,18 @@ public enum InternalAccessArea {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all essential read-only areas.
|
||||||
|
*
|
||||||
|
* @return array containing all essential read-only areas
|
||||||
|
* @since v1-alpha5
|
||||||
|
*/
|
||||||
|
public static InternalAccessArea[] valuesEssentialReadOnly() {
|
||||||
|
return new InternalAccessArea[]{
|
||||||
|
REFLECTIVE_CLASSPATH_SCANNING_GET,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all write-only areas.
|
* Returns all write-only areas.
|
||||||
*
|
*
|
||||||
|
@ -97,8 +136,9 @@ public enum InternalAccessArea {
|
||||||
*/
|
*/
|
||||||
public static InternalAccessArea[] valuesWriteOnly() {
|
public static InternalAccessArea[] valuesWriteOnly() {
|
||||||
return new InternalAccessArea[]{
|
return new InternalAccessArea[]{
|
||||||
SAFETY_SHUTDOWN_HOOK,
|
SAFETY_SHUTDOWN_HOOK_UPDATE,
|
||||||
SHUTDOWN_HANDLER_UPDATE,
|
SHUTDOWN_HANDLER_UPDATE,
|
||||||
|
REFLECTIVE_CLASSPATH_SCANNING_OVERRIDE,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,10 @@ application {
|
||||||
// Force writing to standard output
|
// Force writing to standard output
|
||||||
"-Dsosengine.base.loggerForceStandardOutput=true",
|
"-Dsosengine.base.loggerForceStandardOutput=true",
|
||||||
|
|
||||||
|
// Pass classes which should be included if
|
||||||
|
// reflective sclasspath scanning is disabled.
|
||||||
|
"-Dsosengine.base.initialIncludeSubsystemClasses=de.staropensource.sosengine.ansi.AnsiSubsystem,de.staropensource.sosengine.slf4j_compat.Slf4jCompatSubsystem,de.staropensource.sosengine.windowing.glfw.GlfwSubsystem",
|
||||||
|
|
||||||
// Force Jansi to write escape sequences
|
// Force Jansi to write escape sequences
|
||||||
"-Djansi.mode=force",
|
"-Djansi.mode=force",
|
||||||
]
|
]
|
||||||
|
@ -121,7 +125,9 @@ tasks.register('runNativeImage', Exec) {
|
||||||
args(
|
args(
|
||||||
"-Dsosengine.base.loggerLevel=diagnostic",
|
"-Dsosengine.base.loggerLevel=diagnostic",
|
||||||
"-Dsosengine.base.loggerForceStandardOutput=true",
|
"-Dsosengine.base.loggerForceStandardOutput=true",
|
||||||
"-Djansi.mode=force"
|
"-Dsosengine.base.initialForceDisableClasspathScanning=true",
|
||||||
|
"-Dsosengine.base.initialIncludeSubsystemClasses=de.staropensource.sosengine.ansi.AnsiSubsystem,de.staropensource.sosengine.slf4j_compat.Slf4jCompatSubsystem,de.staropensource.sosengine.windowing.glfw.GlfwSubsystem",
|
||||||
|
"-Djansi.mode=force",
|
||||||
)
|
)
|
||||||
executable("build/bin/sosengine-testapp")
|
executable("build/bin/sosengine-testapp")
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package de.staropensource.sosengine.testapp;
|
||||||
|
|
||||||
import de.staropensource.sosengine.base.Engine;
|
import de.staropensource.sosengine.base.Engine;
|
||||||
import de.staropensource.sosengine.base.annotation.EventListener;
|
import de.staropensource.sosengine.base.annotation.EventListener;
|
||||||
|
import de.staropensource.sosengine.base.implementable.EventListenerCode;
|
||||||
|
import de.staropensource.sosengine.base.implementable.helper.EventHelper;
|
||||||
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
||||||
import de.staropensource.sosengine.base.type.vector.Vec2i;
|
import de.staropensource.sosengine.base.type.vector.Vec2i;
|
||||||
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
||||||
|
@ -113,9 +115,25 @@ public final class Main {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
// Specify subsystems to load
|
||||||
|
System.setProperty(
|
||||||
|
"sosengine.base.initialIncludeSubsystemClasses", (
|
||||||
|
System.getProperty("sosengine.base.initialIncludeSubsystemClasses") == null
|
||||||
|
? "" : System.getProperty("sosengine.base.initialIncludeSubsystemClasses") + ","
|
||||||
|
) + "de.staropensource.sosengine.windowing.WindowingSubsystem"
|
||||||
|
);
|
||||||
|
|
||||||
// Initialize sos!engine
|
// Initialize sos!engine
|
||||||
engine = new Engine();
|
engine = new Engine();
|
||||||
|
|
||||||
|
// Register events
|
||||||
|
EventHelper.registerEvent(InputEvent.class, new EventListenerCode() {
|
||||||
|
@Override
|
||||||
|
public void run(Object... arguments) {
|
||||||
|
onInput((Window) arguments[0], (Key) arguments[1], (KeyState) arguments[2]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Say hello to the world!
|
// Say hello to the world!
|
||||||
logger.info("Hello world!");
|
logger.info("Hello world!");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue