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 { }