Add event precomputation

This commit is contained in:
JeremyStar™ 2024-06-11 22:49:00 +02:00
parent 832ed5cd5c
commit 6be1f7f2cf
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
4 changed files with 125 additions and 20 deletions

View file

@ -19,9 +19,13 @@
package de.staropensource.sosengine.base;
import de.staropensource.sosengine.base.classes.Event;
import de.staropensource.sosengine.base.classes.SubsystemMainClass;
import de.staropensource.sosengine.base.classes.helpers.EventHelper;
import de.staropensource.sosengine.base.data.info.EngineInformation;
import de.staropensource.sosengine.base.events.EngineCrashEvent;
import de.staropensource.sosengine.base.events.EngineShutdownEvent;
import de.staropensource.sosengine.base.events.LogEvent;
import de.staropensource.sosengine.base.events.internal.InternalEngineShutdownEvent;
import de.staropensource.sosengine.base.logging.CrashHandler;
import de.staropensource.sosengine.base.logging.LoggerInstance;
@ -95,6 +99,9 @@ public final class Engine implements SubsystemMainClass {
// Populate crash content
populateCrashContent();
// Precompute event listeners
precomputeEventListeners();
// Initialize variables
logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.ENGINE));
});
@ -171,6 +178,19 @@ public final class Engine implements SubsystemMainClass {
CrashHandler.getCrashContent().put("Stacktrace", "%stacktrace%");
}
/**
* Precomputes all base engine events.
*
* @since 1-alpha0
*/
public void precomputeEventListeners() {
EventHelper.precomputeEventListeners(InternalEngineShutdownEvent.class);
EventHelper.precomputeEventListeners(EngineCrashEvent.class);
EventHelper.precomputeEventListeners(EngineShutdownEvent.class);
EventHelper.precomputeEventListeners(LogEvent.class);
}
/**
* Shuts the engine and JVM down.
*

View file

@ -186,7 +186,7 @@ public final class EngineConfiguration implements SubsystemConfiguration {
private boolean loggerForceStandardOutput;
/**
* Makes the {@link Logger} work asynchronous if enabled.
* If enabled, will makes the {@link Logger} work asynchronous.
*
* @since 1-alpha0
*
@ -197,7 +197,21 @@ public final class EngineConfiguration implements SubsystemConfiguration {
* @see EngineConfiguration#optimizeLogging
* @since 1-alpha0
*/
private boolean optimizeLogging;
private boolean optimizeLogging;
/**
* If enabled, allows for {@link java.util.EventListener} precomputation.
*
* @since 1-alpha0
*
* -- GETTER --
* Gets the value for {@code optimizeEvents}.
*
* @return variable value
* @see EngineConfiguration#optimizeEvents
* @since 1-alpha0
*/
private boolean optimizeEvents;
/**
* Constructor.
@ -248,6 +262,7 @@ public final class EngineConfiguration implements SubsystemConfiguration {
case "loggerForceStandardOutput" -> loggerForceStandardOutput = parser.getBoolean(group + property);
case "optimizeLogging" -> optimizeLogging = parser.getBoolean(group + property);
case "optimizeEvents" -> optimizeEvents = parser.getBoolean(group + property);
}
} catch (NullPointerException ignored) {}
}
@ -277,7 +292,8 @@ public final class EngineConfiguration implements SubsystemConfiguration {
loggerImmediateShutdown = false;
loggerForceStandardOutput = false;
optimizeLogging = false;
optimizeLogging = true;
optimizeEvents = true;
}
/** {@inheritDoc} */
@ -314,6 +330,9 @@ public final class EngineConfiguration implements SubsystemConfiguration {
case "optimizeLogging" -> {
return optimizeLogging;
}
case "optimizeEvents" -> {
return optimizeEvents;
}
default -> {
return null;
}

View file

@ -45,6 +45,13 @@ import java.util.*;
@Getter
@SuppressWarnings({ "unused" })
public class EventHelper {
/**
* Contains all precomputed event listeners.
*
* @since 1-alpha0
*/
private static final HashMap<@NotNull Class<? extends Event>, LinkedList<@NotNull Method>> precomputedEventListeners = new HashMap<>();
/**
* Constructor.
*/
@ -64,6 +71,42 @@ public class EventHelper {
Logger.diag(new LogIssuer(clazz), "Event " + clazz.getName() + " called with arguments " + ListFormatter.formatArray(arguments));
}
/**
* Returns all annotated methods.
*
* @param clazz event class
* @param forceScanning forces the method to ignore precomputed event listeners
* @return list of annotated methods
* @since 1-alpha0
*/
@NotNull
public static LinkedList<Method> getAnnotatedMethods(@NotNull Class<? extends Event> clazz, boolean forceScanning) {
LinkedList<Method> methods = new LinkedList<>();
if (forceScanning || !precomputedEventListeners.containsKey(clazz) || EngineConfiguration.getInstance().isOptimizeEvents()) {
// Scan entire classpath through Reflections library
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 this event out
for (Method method : annotatedMethods)
if (method.getAnnotation(EventListener.class).event() == clazz)
methods.add(method);
// Sort 'methods' linked list
methods.sort(Comparator.comparing(method0 -> method0.getAnnotation(EventListener.class).priority()));
} else
methods = precomputedEventListeners.get(clazz);
return methods;
}
/**
* Returns all annotated methods.
*
@ -73,23 +116,7 @@ public class EventHelper {
*/
@NotNull
public static LinkedList<Method> getAnnotatedMethods(@NotNull Class<? extends Event> clazz) {
LinkedList<Method> methods = new LinkedList<>();
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
.setScanners(Scanners.MethodsAnnotated)
);
Set<@NotNull Method> annotatedMethods = reflections.getMethodsAnnotatedWith(EventListener.class);
for (Method method : annotatedMethods)
if (method.getAnnotation(EventListener.class).event() == clazz)
methods.add(method);
// Sort 'methods' linked list
methods.sort(Comparator.comparing(method0 -> method0.getAnnotation(EventListener.class).priority()));
return methods;
return getAnnotatedMethods(clazz, false);
}
/**
@ -107,4 +134,40 @@ public class EventHelper {
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NullPointerException | ExceptionInInitializerError ignored) {}
}
}
/**
* Precomputes all event listeners listening on some event.
* Will recompute all event listeners if the specified event is already precomputed.
*
* @param clazz event class to (p)recompute, set to {@code null} to recompute all precomputed events
* @since 1-alpha0
*/
public static void precomputeEventListeners(@Nullable Class<? extends Event> clazz) {
if (EngineConfiguration.getInstance().isOptimizeEvents()) return;
if (clazz == null)
for (Class<? extends Event> event : precomputedEventListeners.keySet())
precomputeEventListeners(event);
else {
LinkedList<@NotNull Method> annotatedMethods = getAnnotatedMethods(clazz);
if (precomputedEventListeners.containsKey(clazz))
precomputedEventListeners.replace(clazz, annotatedMethods);
else
precomputedEventListeners.put(clazz, annotatedMethods);
}
}
/**
* Unloads precomputed event listeners.
*
* @param clazz event class to remove precomputed event listeners for, set to {@code null} to remove all precomputed event listeners
* @since 1-alpha0
*/
public static void removePrecomputedEventListeners(@Nullable Class<? extends Event> clazz) {
if (clazz == null)
precomputedEventListeners.clear();
else
precomputedEventListeners.remove(clazz);
}
}

View file

@ -58,6 +58,9 @@ public final class LogEvent implements Event {
* @since 1-alpha0
*/
public void callEvent(@NotNull LogLevel level, @NotNull LogIssuer logIssuer, @NotNull String message) {
// Uncommenting this would be a great way to cause a StackOverflowException!
//EventHelper.logCall(getClass(), level, logIssuer, message);
for (Method method : EventHelper.getAnnotatedMethods(getClass())) {
try {
method.invoke(null, level, logIssuer, message);