From b2171717ad7c9bd49748a3e93c8e25ae0638e52e Mon Sep 17 00:00:00 2001 From: Jeremy Wood Date: Sat, 11 Mar 2023 11:37:33 -0500 Subject: [PATCH] Add API for creating auto-registering services. --- .../AbstractAutoRegistration.java | 69 +++++++++++++++++++ .../registration/AutoRegistrationBinder.java | 34 +++++++++ .../inject/registration/DoNotRegister.java | 19 +++++ .../DoNotRegisterRegistrationFilter.java | 12 ++++ .../registration/RegistrationFilter.java | 14 ++++ 5 files changed, 148 insertions(+) create mode 100644 src/main/java/com/onarandombox/MultiverseCore/inject/registration/AbstractAutoRegistration.java create mode 100644 src/main/java/com/onarandombox/MultiverseCore/inject/registration/AutoRegistrationBinder.java create mode 100644 src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegister.java create mode 100644 src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegisterRegistrationFilter.java create mode 100644 src/main/java/com/onarandombox/MultiverseCore/inject/registration/RegistrationFilter.java diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AbstractAutoRegistration.java b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AbstractAutoRegistration.java new file mode 100644 index 00000000..c5510df7 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AbstractAutoRegistration.java @@ -0,0 +1,69 @@ +package com.onarandombox.MultiverseCore.inject.registration; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.glassfish.hk2.api.Filter; +import org.glassfish.hk2.api.InstanceLifecycleEvent; +import org.glassfish.hk2.api.InstanceLifecycleEventType; +import org.glassfish.hk2.api.InstanceLifecycleListener; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +/** + * Provided as a base class for {@link InstanceLifecycleListener} implementations that automatically register instances + * of a given type. + *
+ * This will cause any instance of the given type to be registered as a service unless it is annotated with + * {@link DoNotRegister}. What registration means is up to the implementation. + *
+ * Note: Implementations should be annotated with {@link Service} and utilize constructor injection with {@link Inject} + * to ensure that the filter provider is properly injected. + * + * @param The type of instances to automatically register. + */ +public abstract class AbstractAutoRegistration implements InstanceLifecycleListener { + + private final Provider filterProvider; + private final Class autoRegisteredType; + + protected AbstractAutoRegistration( + @NotNull Provider filterProvider, + @NotNull Class autoRegisteredType + ) { + this.filterProvider = filterProvider; + this.autoRegisteredType = autoRegisteredType; + } + + private RegistrationFilter getRegistrationFilter() { + return filterProvider.get(); + } + + @Override + public Filter getFilter() { + return null; + } + + @Override + public void lifecycleEvent(InstanceLifecycleEvent lifecycleEvent) { + if (lifecycleEvent.getEventType() != InstanceLifecycleEventType.POST_PRODUCTION) { + return; + } + var potentialInstance = lifecycleEvent.getLifecycleObject(); + if (shouldRegister(potentialInstance)) { + register(autoRegisteredType.cast(potentialInstance)); + } + } + + private boolean shouldRegister(Object instance) { + return autoRegisteredType.isInstance(instance) && getRegistrationFilter().shouldRegister(instance); + } + + /** + * Called when the given instance should be registered. What registration means is up to the implementation. + * If the instance's class is annotated with {@link DoNotRegister}, this method will not be called for instances of + * that type. + * + * @param instance The instance to register. + */ + protected abstract void register(T instance); +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AutoRegistrationBinder.java b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AutoRegistrationBinder.java new file mode 100644 index 00000000..81f0eabf --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/AutoRegistrationBinder.java @@ -0,0 +1,34 @@ +package com.onarandombox.MultiverseCore.inject.registration; + +import jakarta.inject.Singleton; +import org.glassfish.hk2.api.InstanceLifecycleListener; +import org.glassfish.hk2.utilities.binding.AbstractBinder; + +/** + * A binder that binds an implementation of {@link InstanceLifecycleListener} to the {@link InstanceLifecycleListener} + * contract. Additionally, it performs the necessary bindings to support the {@link DoNotRegister} annotation. + */ +public class AutoRegistrationBinder extends AbstractBinder { + + /** + * Creates a new {@link AutoRegistrationBinder}. + * + * @param lifecycleListenerClass The implementation of {@link InstanceLifecycleListener} to bind + * @return The new {@link AutoRegistrationBinder} + */ + public static AutoRegistrationBinder with(Class lifecycleListenerClass) { + return new AutoRegistrationBinder(lifecycleListenerClass); + } + + private final Class lifecycleListenerClass; + + private AutoRegistrationBinder(Class lifecycleListenerClass) { + this.lifecycleListenerClass = lifecycleListenerClass; + } + + @Override + protected void configure() { + bind(DoNotRegisterRegistrationFilter.class).in(Singleton.class).to(RegistrationFilter.class).ranked(-1); + bind(lifecycleListenerClass).in(Singleton.class).to(InstanceLifecycleListener.class); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegister.java b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegister.java new file mode 100644 index 00000000..bcd2bb85 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegister.java @@ -0,0 +1,19 @@ +package com.onarandombox.MultiverseCore.inject.registration; + +import org.bukkit.event.Listener; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation that can be used to prevent a class that would normally be automatically registered from being + * registered automatically. + *
+ * For example, any {@link Listener} would normally be registered automatically, but if it is annotated with + * {@code @DoNotRegister}, it will not be registered automatically. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface DoNotRegister { } diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegisterRegistrationFilter.java b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegisterRegistrationFilter.java new file mode 100644 index 00000000..a743dc74 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/DoNotRegisterRegistrationFilter.java @@ -0,0 +1,12 @@ +package com.onarandombox.MultiverseCore.inject.registration; + +import org.jvnet.hk2.annotations.Service; + +@Service +class DoNotRegisterRegistrationFilter implements RegistrationFilter { + + @Override + public boolean shouldRegister(Object object) { + return object.getClass().getAnnotation(DoNotRegister.class) == null; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/registration/RegistrationFilter.java b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/RegistrationFilter.java new file mode 100644 index 00000000..cd667fa3 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/registration/RegistrationFilter.java @@ -0,0 +1,14 @@ +package com.onarandombox.MultiverseCore.inject.registration; + +import org.jvnet.hk2.annotations.Contract; + +/** + * The base contract for automatic registration filters. + *
+ * This is primarily for internal use only. See {@link DoNotRegister} for a practical application. + */ +@Contract +public interface RegistrationFilter { + + boolean shouldRegister(Object object); +}