Add PluginService and InjectionFeature loading.

This commit is contained in:
Jeremy Wood 2023-03-10 02:07:44 -05:00
parent b40ded9806
commit 9f650d683d
No known key found for this signature in database
GPG Key ID: C5BAD04C77B91B4B
3 changed files with 77 additions and 19 deletions

View File

@ -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.
* <br/>
* 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.
* <br/>
* 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.
* <br/>
* All {@link PluginService} instances should be created by this point.
*
* @param pluginServiceLocator The service locator for the plugin.
*/
default void postServicesCreation(ServiceLocator pluginServiceLocator) {}
}

View File

@ -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.
* <br/>
@ -36,9 +38,16 @@ public final class PluginInjection {
@NotNull
public static Try<ServiceLocator> 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<ServiceLocator> load(@NotNull PluginBinder<?> pluginBinder) {
var plugin = pluginBinder.getPlugin();
private Try<ServiceLocator> load(List<InjectionFeature> 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<InjectionFeature> features) throws MultiException {
features.forEach(feature -> feature.preServicesCreation(pluginServiceLocator));
pluginServiceLocator.getAllServices(PluginService.class);
features.forEach(feature -> feature.postServicesCreation(pluginServiceLocator));
}
@NotNull
private static Try<ServiceLocator> 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

View File

@ -0,0 +1,11 @@
package com.onarandombox.MultiverseCore.inject;
import org.jvnet.hk2.annotations.Contract;
/**
* Marker interface for plugin services.
* <br/>
* Implementations of this interface will be eagerly loaded when injection is initialized for the owning plugin.
*/
@Contract
public interface PluginService { }