From 2bf3e14ab91af8bd2dfd5bab0063dc27ae909b6d Mon Sep 17 00:00:00 2001 From: asofold Date: Thu, 16 Jun 2016 22:23:54 +0200 Subject: [PATCH] More on GenericInstanceRegistry. * Add a way to stay updated about the latest registration state for a class. * Add a class for the registry. * Let the registry log all registrations. * Make super interfaces for LogManager (simple logging of String/Throwable). Missing: * More streams (REGISTRY, PLAYER/CHECK_STATUS/EVENTS or just CHECKS at least). Make status rather the plugin status. Registry could have an extra file. * More efficient IGenericInstanceHandle use (wrap + reference counting). --- .../nocheatplus/logging/LogManager.java | 25 +-- .../logging/details/IGetStreamId.java | 16 ++ .../logging/details/ILogString.java | 25 +++ .../logging/details/ILogThrowable.java | 25 +++ .../registry/GenericInstanceRegistry.java | 81 ++++++---- .../event/IGenericInstanceHandle.java | 39 +++++ .../IGenericInstanceRegistryListener.java | 48 ++++++ .../IUnregisterGenericInstanceListener.java | 13 ++ .../neatmonster/nocheatplus/NoCheatPlus.java | 56 ++++--- .../compat/registry/RegistryHelper.java | 1 - .../DefaultGenericInstanceRegistry.java | 153 ++++++++++++++++++ .../registry/event/GenericInstanceHandle.java | 89 ++++++++++ .../neatmonster/nocheatplus/PluginTests.java | 6 + 13 files changed, 502 insertions(+), 75 deletions(-) create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/IGetStreamId.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogString.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogThrowable.java create mode 100644 NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceHandle.java create mode 100644 NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceRegistryListener.java create mode 100644 NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IUnregisterGenericInstanceListener.java create mode 100644 NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java create mode 100644 NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/LogManager.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/LogManager.java index e40e7684..ef6c53e8 100644 --- a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/LogManager.java +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/LogManager.java @@ -14,7 +14,8 @@ */ package fr.neatmonster.nocheatplus.logging; -import java.util.logging.Level; +import fr.neatmonster.nocheatplus.logging.details.ILogString; +import fr.neatmonster.nocheatplus.logging.details.ILogThrowable; /** * Central access point log manager with a bias towards String messages. @@ -22,7 +23,7 @@ import java.util.logging.Level; * @author dev1mc * */ -public interface LogManager { +public interface LogManager extends ILogString, ILogThrowable { /** * A stream that skips all messages. It's not registered officially. @@ -99,24 +100,4 @@ public interface LogManager { */ public StreamID getStreamID(String name); - void debug(StreamID streamID, String message); - - void info(StreamID streamID, String message); - - void warning(StreamID streamID, String message); - - void severe(StreamID streamID, String message); - - void log(StreamID streamID, Level level, String message); - - void debug(StreamID streamID, Throwable t); - - void info(StreamID streamID, Throwable t); - - void warning(StreamID streamID, Throwable t); - - void severe(StreamID streamID, Throwable t); - - void log(StreamID streamID, Level level, Throwable t); - } diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/IGetStreamId.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/IGetStreamId.java new file mode 100644 index 00000000..6631c060 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/IGetStreamId.java @@ -0,0 +1,16 @@ +package fr.neatmonster.nocheatplus.logging.details; + +import fr.neatmonster.nocheatplus.logging.StreamID; + +/** + * Allow to select/return a StgreamID instance. + * + * @author asofold + * + * @param + */ +public interface IGetStreamId { + + public StreamID getStreamId(); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogString.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogString.java new file mode 100644 index 00000000..cf749ee7 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogString.java @@ -0,0 +1,25 @@ +package fr.neatmonster.nocheatplus.logging.details; + +import java.util.logging.Level; + +import fr.neatmonster.nocheatplus.logging.StreamID; + +/** + * Standard logging for String messages. + * + * @author asofold + * + */ +public interface ILogString { + + void debug(StreamID streamID, String message); + + void info(StreamID streamID, String message); + + void warning(StreamID streamID, String message); + + void severe(StreamID streamID, String message); + + void log(StreamID streamID, Level level, String message); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogThrowable.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogThrowable.java new file mode 100644 index 00000000..1afc0ce3 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/logging/details/ILogThrowable.java @@ -0,0 +1,25 @@ +package fr.neatmonster.nocheatplus.logging.details; + +import java.util.logging.Level; + +import fr.neatmonster.nocheatplus.logging.StreamID; + +/** + * Standard logging for Throwable throwables. + * + * @author asofold + * + */ +public interface ILogThrowable { + + void debug(StreamID streamID, Throwable t); + + void info(StreamID streamID, Throwable t); + + void warning(StreamID streamID, Throwable t); + + void severe(StreamID streamID, Throwable t); + + void log(StreamID streamID, Level level, Throwable t); + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/GenericInstanceRegistry.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/GenericInstanceRegistry.java index 9d6ead94..97e4355a 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/GenericInstanceRegistry.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/GenericInstanceRegistry.java @@ -14,44 +14,63 @@ */ package fr.neatmonster.nocheatplus.components.registry; +import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; + /** * A registry for unique instances of any class type.
* Currently there is no specification for what happens with registering for an * already registered class, neither if exceptions are thrown, nor if * dependencies will use those then. * - * @author dev1mc + * @author asofold * */ public interface GenericInstanceRegistry { - - /** - * Register the instance by its own class. - * @param instance - */ - public T registerGenericInstance(T instance); - - /** - * Register an instance under for a super-class. - * @todo The registry implementation might specify if overriding is allowed. - * @param registerAs - * @param instance - * @return The previously registered instance. If none was registered, null is returned. - */ - public T registerGenericInstance(Class registerFor, TI instance); - - /** - * Retrieve the instance registered for the given class. - * @param registeredBy - * @return The instance, or null, if none is registered. - */ - public T getGenericInstance(Class registeredFor); - - /** - * Remove a registration. The registry implementation might specify id removing is allowed. - * @param registeredFor - * @return The previously registered instance. If none was registered, null is returned. - */ - public T unregisterGenericInstance(Class registeredFor); - + + /** + * Register the instance by its own class. This demands type parameters to + * be aligned to the actual class. + * + * @param instance + */ + public T registerGenericInstance(T instance); + + /** + * Register an instance under for a super-class. + * + * @todo The registry implementation might specify if overriding is allowed. + * @param registerAs + * @param instance + * @return The previously registered instance. If none was registered, null + * is returned. + */ + public T registerGenericInstance(Class registerFor, TI instance); + + /** + * Retrieve the instance registered for the given class. + * + * @param registeredBy + * @return The instance, or null, if none is registered. + */ + public T getGenericInstance(Class registeredFor); + + /** + * Remove a registration. The registry implementation might specify id + * removing is allowed. + * + * @param registeredFor + * @return The previously registered instance. If none was registered, null + * is returned. + */ + public T unregisterGenericInstance(Class registeredFor); + + /** + * Get a self-updating handle for conveniently getting the currently + * registered instance. + * + * @param registeredFor + * @return + */ + public IGenericInstanceHandle getGenericInstanceHandle(Class registeredFor); + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceHandle.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceHandle.java new file mode 100644 index 00000000..6abcc05e --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceHandle.java @@ -0,0 +1,39 @@ +package fr.neatmonster.nocheatplus.components.registry.event; + +/** + * Convenience to retrieve the currently registered instance. Note that + * registrations by other plugins might be problematic, thus removing + * registrations and stored IGenericInstanceHandle instances is within the + * responsibility of the hooking plugin. + * + * @author asofold + * + * @param + * The type instances are registered for. + */ +public interface IGenericInstanceHandle { + + // TODO: ? + + /** + * Get the currently registered instance. + * + * @return + * @throws RuntimeException, + * if disableHandle has been called. + */ + public T getHandle(); + + /** + * Unlink from the registry. Subsequent calls to getHandle will yield a + * RuntimeException, while disableHandle can still be called without effect. + * This may not be necessary, if the registration lasts during an entire + * runtime, however if an object that holds IGenericInstanceHandle instances + * gets overridden on reloading the configuration of the plugin, keeping + * handles may leak a little bit of memory and increase CPU load with each + * time such happens. Often changing registration is not a typical use-case. + * This can not be undone. + */ + public void disableHandle(); + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceRegistryListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceRegistryListener.java new file mode 100644 index 00000000..c4005d52 --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IGenericInstanceRegistryListener.java @@ -0,0 +1,48 @@ +package fr.neatmonster.nocheatplus.components.registry.event; + +/** + * Receive Notifications about generic instance registry events. Listeners may + * be possible to register even before an actual instance has been registered + * (and even before it's known to the registry that anything is intended to be + * registered) - subject to change. + * + * @author asofold + * + */ +public interface IGenericInstanceRegistryListener { + + // TODO: ? + + /** + * Registration, without an entry being present. + * + * @param registerFor + * @param instance + * Might be null, if the registry allows that. + */ + public void onGenericInstanceRegister(Class registerFor, T instance); + + /** + * An already registered entry gets overridden. + * + * @param registerFor + * @param newInstance + * The instance that just got registered. Might be null, if the + * registry allows that. + * @param oldInstance + * The instance that had been registered before. Might be null, + * if the registry allows that. + */ + public void onGenericInstanceOverride(Class registerFor, T newInstance, T oldInstance); + + /** + * A registration is removed explicitly. + * + * @param registerFor + * @param oldInstance + * The instance that had been registered before. Might be null, + * if the registry allows that. + */ + public void onGenericInstanceRemove(Class registerFor, T oldInstance); + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IUnregisterGenericInstanceListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IUnregisterGenericInstanceListener.java new file mode 100644 index 00000000..edace3ff --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/IUnregisterGenericInstanceListener.java @@ -0,0 +1,13 @@ +package fr.neatmonster.nocheatplus.components.registry.event; + +/** + * Rather an internal interface. + * + * @author asofold + * + */ +public interface IUnregisterGenericInstanceListener { + + public void unregisterGenericInstanceListener(Class registeredFor, IGenericInstanceHandle listener); + +} diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java index b6b6fccd..bd60d0b8 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java @@ -82,6 +82,8 @@ import fr.neatmonster.nocheatplus.compat.versions.GenericVersion; import fr.neatmonster.nocheatplus.compat.versions.ServerVersion; import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI; import fr.neatmonster.nocheatplus.components.registry.ComponentRegistry; +import fr.neatmonster.nocheatplus.components.registry.DefaultGenericInstanceRegistry; +import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName; import fr.neatmonster.nocheatplus.components.registry.feature.ConsistencyChecker; import fr.neatmonster.nocheatplus.components.registry.feature.DisableListener; @@ -110,7 +112,9 @@ import fr.neatmonster.nocheatplus.hooks.allviolations.AllViolationsHook; import fr.neatmonster.nocheatplus.logging.BukkitLogManager; import fr.neatmonster.nocheatplus.logging.LogManager; import fr.neatmonster.nocheatplus.logging.StaticLog; +import fr.neatmonster.nocheatplus.logging.StreamID; import fr.neatmonster.nocheatplus.logging.Streams; +import fr.neatmonster.nocheatplus.logging.details.IGetStreamId; import fr.neatmonster.nocheatplus.permissions.PermissionUtil; import fr.neatmonster.nocheatplus.permissions.PermissionUtil.CommandProtectionEntry; import fr.neatmonster.nocheatplus.permissions.Permissions; @@ -184,9 +188,6 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { /** The event listeners. */ private final List listeners = new ArrayList(); - /** Storage for generic instances registration. */ - private final Map, Object> genericInstances = new HashMap, Object>(); - /** Components that need notification on reloading. * (Kept here, for if during runtime some might get added.)*/ private final List notifyReload = new LinkedList(); @@ -230,6 +231,9 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { /** Listener for the BlockChangeTracker (register once, lazy). */ private BlockChangeListener blockChangeListener = null; + private final DefaultGenericInstanceRegistry genericInstanceRegistry = new DefaultGenericInstanceRegistry(); + + /** Tick listener that is only needed sometimes (component registration). */ protected final OnDemandTickListener onDemandTickListener = new OnDemandTickListener() { @Override @@ -262,6 +266,12 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { private boolean clearExemptionsOnJoin = true; private boolean clearExemptionsOnLeave = true; + private StreamID getRegistryStreamId() { + // TODO: Select by config, or add Streams.REGISTRY for a new default. + // For now prefer log file, unless extended status is set. + return ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS) ? Streams.STATUS : Streams.DEFAULT_FILE; + } + /** * Remove expired entries. */ @@ -737,7 +747,7 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { // Just in case: clear the subComponentHolders. subComponentholders.clear(); // Generic instances registry. - genericInstances.clear(); + genericInstanceRegistry.clear(); // Feature tags. featureTags.clear(); // BlockChangeTracker. @@ -850,6 +860,14 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { StaticLog.setUseLogManager(true); logManager.info(Streams.INIT, "Logging system initialized."); logManager.info(Streams.INIT, "Detected Minecraft version: " + ServerVersion.getMinecraftVersion()); + genericInstanceRegistry.setLogger( + logManager, new IGetStreamId() { + @Override + public StreamID getStreamId() { + // TODO Auto-generated method stub + return NoCheatPlus.this.getRegistryStreamId(); + } + }, "[GenericInstanceRegistry] "); } } @@ -1468,33 +1486,29 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { } } + @Override + public T getGenericInstance(Class registeredFor) { + return genericInstanceRegistry.getGenericInstance(registeredFor); + } + @Override public T registerGenericInstance(T instance) { - @SuppressWarnings("unchecked") - Class clazz = (Class) instance.getClass(); - T registered = getGenericInstance(clazz); - genericInstances.put(clazz, instance); - return registered; + return genericInstanceRegistry.registerGenericInstance(instance); } @Override public T registerGenericInstance(Class registerFor, TI instance) { - T registered = getGenericInstance(registerFor); - genericInstances.put(registerFor, instance); - return registered; - } - - @SuppressWarnings("unchecked") - @Override - public T getGenericInstance(Class registeredFor) { - return (T) genericInstances.get(registeredFor); + return genericInstanceRegistry.registerGenericInstance(registerFor, instance); } @Override public T unregisterGenericInstance(Class registeredFor) { - T registered = getGenericInstance(registeredFor); // Convenience. - genericInstances.remove(registeredFor); - return registered; + return genericInstanceRegistry.unregisterGenericInstance(registeredFor); + } + + @Override + public IGenericInstanceHandle getGenericInstanceHandle(Class registeredFor) { + return genericInstanceRegistry.getGenericInstanceHandle(registeredFor); } @Override diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/registry/RegistryHelper.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/registry/RegistryHelper.java index 923196eb..c920b31b 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/registry/RegistryHelper.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/registry/RegistryHelper.java @@ -141,7 +141,6 @@ public class RegistryHelper { public static T registerGenericInstance(Class registerFor, ET result) { if (result != null) { NCPAPIProvider.getNoCheatPlusAPI().registerGenericInstance(registerFor, result); - NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS, "Registered for " + registerFor.getName() + ": " + result.getClass().getName()); } else { NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS, "Could not register an instance for: " + registerFor.getName()); diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java new file mode 100644 index 00000000..19342d5a --- /dev/null +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java @@ -0,0 +1,153 @@ +package fr.neatmonster.nocheatplus.components.registry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import fr.neatmonster.nocheatplus.components.registry.event.GenericInstanceHandle; +import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; +import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceRegistryListener; +import fr.neatmonster.nocheatplus.components.registry.event.IUnregisterGenericInstanceListener; +import fr.neatmonster.nocheatplus.logging.details.IGetStreamId; +import fr.neatmonster.nocheatplus.logging.details.ILogString; + +public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, IUnregisterGenericInstanceListener { + + /** Storage for generic instances registration. */ + private final Map, Object> instances = new HashMap, Object>(); + + /** Listeners for registry events. */ + private final Map, Collection>> listeners = new HashMap, Collection>>(); + + /** Handles created within this class, that have to be detached. */ + private final Set> ownedHandles = new LinkedHashSet>(); + + private ILogString logger = null; + + private IGetStreamId selectStream; + + private String logPrefix; + + public void setLogger(ILogString logger, IGetStreamId selectStream, String logPrefix) { + this.logger = logger; + this.selectStream = selectStream; + this.logPrefix = logPrefix; + } + + @Override + public void unregisterGenericInstanceListener(Class registeredFor, IGenericInstanceHandle listener) { + Collection> registered = listeners.get(registeredFor); + if (registered != null) { + registered.remove(listener); + if (registered.isEmpty()) { + listeners.remove(registeredFor); + } + } + if ((listener instanceof IGenericInstanceHandle) && ownedHandles.contains(listener)) { + ownedHandles.remove(listener); + ((IGenericInstanceHandle) listener).disableHandle(); + } + } + + @SuppressWarnings("unchecked") + @Override + public T registerGenericInstance(T instance) { + return registerGenericInstance((Class) instance.getClass(), instance); + } + + @SuppressWarnings("unchecked") + @Override + public T registerGenericInstance(Class registerFor, TI instance) { + T registered = getGenericInstance(registerFor); + final boolean had = instances.containsKey(registerFor); + instances.put(registerFor, instance); + Collection> registeredListeners = listeners.get(registerFor); + if (registeredListeners != null) { + for (IGenericInstanceRegistryListener rawListener : registeredListeners) { + if (had) { + ((IGenericInstanceRegistryListener) rawListener).onGenericInstanceOverride(registerFor, instance, registered); + } + else { + ((IGenericInstanceRegistryListener) rawListener).onGenericInstanceRegister(registerFor, instance); + } + } + } + if (had) { + logRegistryEvent("Registered (override) for " + registerFor.getName() + ": " + instance.getClass().getName()); + } + else { + logRegistryEvent("Registered for " + registerFor.getName() + ": " + instance.getClass().getName()); + } + return registered; + } + + @SuppressWarnings("unchecked") + @Override + public T getGenericInstance(Class registeredFor) { + return (T) instances.get(registeredFor); + } + + @SuppressWarnings("unchecked") + @Override + public T unregisterGenericInstance(Class registeredFor) { + T registered = getGenericInstance(registeredFor); // Convenience. + final boolean had = instances.containsKey(registeredFor); + instances.remove(registeredFor); + Collection> registeredListeners = listeners.get(registeredFor); + if (registeredListeners != null) { + for (IGenericInstanceRegistryListener rawListener : registeredListeners) { + ((IGenericInstanceRegistryListener) rawListener).onGenericInstanceRemove(registeredFor, registered); + } + } + if (had) { + logRegistryEvent("Unregister, remove mapping for: " + registeredFor.getName()); + } + else { + logRegistryEvent("Unregister, no mapping present for: " + registeredFor.getName()); + } + return registered; + } + + @Override + public IGenericInstanceHandle getGenericInstanceHandle(Class registeredFor) { + /* + * More efficient should be to return a wrapper for a unique instance, + * for which disableHandle runs once, so only one listener per + * registered class is necessary, which then uses reference counting for + * actual removal. That's double-wrapped then (returned instance disable + * once -> reference counting instance -> actual instance). + */ + final IGenericInstanceHandle handle = new GenericInstanceHandle(registeredFor, this, this); + ownedHandles.add(handle); + Collection> registered = listeners.get(registeredFor); + if (registered == null) { + registered = new HashSet>(); + listeners.put(registeredFor, registered); + } + registered.add((IGenericInstanceRegistryListener) handle); + return handle; + } + + public void clear() { + instances.clear(); + listeners.clear(); + // TODO: consider fire unregister or add a removal method ? + // Force detach all handles. + for (IGenericInstanceHandle handle : new ArrayList>(ownedHandles)) { + handle.disableHandle(); + } + ownedHandles.clear(); + logRegistryEvent("Registry cleared."); + } + + protected void logRegistryEvent(String message) { + if (logger != null) { + logger.info(selectStream.getStreamId(), logPrefix == null ? message : logPrefix + message); + } + } + +} diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java new file mode 100644 index 00000000..66b0a919 --- /dev/null +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java @@ -0,0 +1,89 @@ +package fr.neatmonster.nocheatplus.components.registry.event; + +import fr.neatmonster.nocheatplus.components.registry.GenericInstanceRegistry; + +/** + * Default implementation for retrieving a IGenericInstanceHandle from a + * registry. + * + * @author asofold + * + * @param + */ +public class GenericInstanceHandle implements IGenericInstanceRegistryListener, IGenericInstanceHandle { + + // TODO: ? + // TODO: Might move to NCPPlugin, or later split (mostly) interface based api from default implementations. + + private GenericInstanceRegistry registry; + private IUnregisterGenericInstanceListener unregister; + private Class registeredFor; + private T handle = null; + private boolean initialized = false; + private boolean disabled = false; + + // TODO: Remove method? + + /** + * Note that this doesn't register with the registry, as the registry may + * return unique handles on request rather. + * + * @param registeredFor + * @param registry + * @param unregister + */ + public GenericInstanceHandle(Class registeredFor, GenericInstanceRegistry registry, IUnregisterGenericInstanceListener unregister) { + this.registry = registry; + this.unregister = unregister; + this.registeredFor = registeredFor; + } + + private T fetchHandle() { + return registry.getGenericInstance(registeredFor); + } + + @Override + public void onGenericInstanceRegister(Class registerFor, T instance) { + this.handle = instance; + initialized = true; + } + + @Override + public void onGenericInstanceOverride(Class registerFor, T newInstance, T oldInstance) { + this.handle = newInstance; + initialized = true; + } + + @Override + public void onGenericInstanceRemove(Class registerFor, T oldInstance) { + this.handle = null; + initialized = true; + } + + @Override + public T getHandle() { + if (initialized) { + return handle; + } + else if (disabled) { + throw new RuntimeException("Already disabled."); + } + else { + return fetchHandle(); + } + } + + @Override + public void disableHandle() { + if (unregister != null) { + disabled = true; + initialized = false; + handle = null; + registeredFor = null; + registry = null; + unregister.unregisterGenericInstanceListener(registeredFor, this); + unregister = null; + } + } + +} diff --git a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/PluginTests.java b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/PluginTests.java index 6320dd9a..59ba716a 100644 --- a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/PluginTests.java +++ b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/PluginTests.java @@ -23,6 +23,7 @@ import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; import fr.neatmonster.nocheatplus.compat.bukkit.MCAccessBukkit; import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI; import fr.neatmonster.nocheatplus.components.registry.ComponentRegistry; +import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; import fr.neatmonster.nocheatplus.logging.LogManager; import fr.neatmonster.nocheatplus.logging.StaticLog; @@ -82,6 +83,11 @@ public class PluginTests { throw new UnsupportedOperationException(); } + @Override + public IGenericInstanceHandle getGenericInstanceHandle(Class registeredFor) { + throw new UnsupportedOperationException(); + } + @Override public boolean addComponent(Object obj, boolean allowComponentFactory) { throw new UnsupportedOperationException();