diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/InjectionFeature.java b/src/main/java/com/onarandombox/MultiverseCore/inject/InjectionFeature.java new file mode 100644 index 00000000..4d86a4eb --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/InjectionFeature.java @@ -0,0 +1,33 @@ +package com.onarandombox.MultiverseCore.inject; + +import org.glassfish.hk2.api.ServiceLocator; +import org.jvnet.hk2.annotations.Contract; + +/** + * Marker interface for injection features. + *
+ * Injection features are used to extend the functionality of the {@link PluginInjection} class. They are only used + * internally and should not be implemented by plugins. + */ +@Contract +public interface InjectionFeature { + + /** + * Called prior to the eager loading of {@link PluginService}s. + *
+ * It's possible that performing injection in feature related services will cause {@link PluginService} instances to + * be created. + * + * @param pluginServiceLocator The service locator for the plugin. + */ + default void preServicesCreation(ServiceLocator pluginServiceLocator) {} + + /** + * Called after the eager loading of {@link PluginService}s. + *
+ * All {@link PluginService} instances should be created by this point. + * + * @param pluginServiceLocator The service locator for the plugin. + */ + default void postServicesCreation(ServiceLocator pluginServiceLocator) {} +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/PluginInjection.java b/src/main/java/com/onarandombox/MultiverseCore/inject/PluginInjection.java index 8d6f8c70..e2ba8468 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/inject/PluginInjection.java +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/PluginInjection.java @@ -3,10 +3,10 @@ package com.onarandombox.MultiverseCore.inject; import com.onarandombox.MultiverseCore.inject.binder.JavaPluginBinder; import com.onarandombox.MultiverseCore.inject.binder.PluginBinder; import com.onarandombox.MultiverseCore.inject.binder.ServerBinder; -import io.vavr.control.Option; import io.vavr.control.Try; import org.bukkit.plugin.Plugin; import org.glassfish.hk2.api.DynamicConfigurationService; +import org.glassfish.hk2.api.MultiException; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.api.ServiceLocatorFactory; import org.glassfish.hk2.internal.ServiceLocatorFactoryImpl; @@ -14,6 +14,8 @@ import org.glassfish.hk2.utilities.ClasspathDescriptorFileFinder; import org.glassfish.hk2.utilities.ServiceLocatorUtilities; import org.jetbrains.annotations.NotNull; +import java.util.List; + /** * Provides methods to set up dependency injection for plugins. *
@@ -36,9 +38,16 @@ public final class PluginInjection { @NotNull public static Try createServiceLocator(@NotNull PluginBinder pluginBinder) { var factory = new ServiceLocatorFactoryImpl(); - return createServerServiceLocator(factory) - .map(locator -> new PluginInjection(factory, locator)) - .flatMap(pluginInjection -> pluginInjection.load(pluginBinder)); + + var systemServiceLocator = createSystemServiceLocator(factory); + + var features = systemServiceLocator + .mapTry(locator -> locator.getAllServices(InjectionFeature.class)); + + return systemServiceLocator + .flatMap(systemLocator -> createServerServiceLocator(factory, systemLocator)) + .map(serverLocator -> new PluginInjection(pluginBinder, factory, serverLocator)) + .flatMap(pluginInjection -> features.flatMap(pluginInjection::load)); } /** @@ -67,34 +76,39 @@ public final class PluginInjection { pluginServiceLocator.shutdown(); } - private final ServiceLocatorFactory serviceLocatorFactory; - private final ServiceLocator serverServiceLocator; + private final PluginBinder pluginBinder; + private final Plugin plugin; + private final ServiceLocator pluginServiceLocator; private PluginInjection( + @NotNull PluginBinder pluginBinder, @NotNull ServiceLocatorFactory serviceLocatorFactory, @NotNull ServiceLocator serverServiceLocator ) { - this.serviceLocatorFactory = serviceLocatorFactory; - this.serverServiceLocator = serverServiceLocator; + this.pluginBinder = pluginBinder; + plugin = pluginBinder.getPlugin(); + pluginServiceLocator = serviceLocatorFactory.create(plugin.getName(), serverServiceLocator); } - private Try load(@NotNull PluginBinder pluginBinder) { - var plugin = pluginBinder.getPlugin(); + private Try load(List features) { + return Try.runRunnable(() -> ServiceLocatorUtilities.bind(pluginServiceLocator, pluginBinder)) + .flatMap(ignored -> populatePluginServiceLocator(pluginServiceLocator, plugin)) + .andThenTry(() -> loadAncillaryServices(features)); + } - return Option.of(serviceLocatorFactory.create(plugin.getName(), serverServiceLocator)) - .toTry() - .andThenTry(pluginServiceLocator -> ServiceLocatorUtilities.bind(pluginServiceLocator, pluginBinder)) - .flatMap(pluginServiceLocator -> populatePluginServiceLocator(pluginServiceLocator, plugin)); + private void loadAncillaryServices(List features) throws MultiException { + features.forEach(feature -> feature.preServicesCreation(pluginServiceLocator)); + pluginServiceLocator.getAllServices(PluginService.class); + features.forEach(feature -> feature.postServicesCreation(pluginServiceLocator)); } @NotNull private static Try createServerServiceLocator( - @NotNull ServiceLocatorFactory serviceLocatorFactory + @NotNull ServiceLocatorFactory serviceLocatorFactory, + @NotNull ServiceLocator systemServiceLocator ) { - return createSystemServiceLocator(serviceLocatorFactory) - .map(systemServiceLocator -> serviceLocatorFactory.create("server", systemServiceLocator)) - .andThenTry(serverServiceLocator -> - ServiceLocatorUtilities.bind(serverServiceLocator, new ServerBinder())); + return Try.of(() -> serviceLocatorFactory.create("server", systemServiceLocator)) + .andThenTry(locator -> ServiceLocatorUtilities.bind(locator, new ServerBinder())); } @NotNull diff --git a/src/main/java/com/onarandombox/MultiverseCore/inject/PluginService.java b/src/main/java/com/onarandombox/MultiverseCore/inject/PluginService.java new file mode 100644 index 00000000..ed179e24 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/inject/PluginService.java @@ -0,0 +1,11 @@ +package com.onarandombox.MultiverseCore.inject; + +import org.jvnet.hk2.annotations.Contract; + +/** + * Marker interface for plugin services. + *
+ * Implementations of this interface will be eagerly loaded when injection is initialized for the owning plugin. + */ +@Contract +public interface PluginService { }