Plugins: Add components creator as bridge between guice and new plugin init world

This change adds a createComponents() method to Plugin implementations
which they can use to return already constructed componenents/services.
Eventually this should be just services ("components" don't really do
anything), but for now it allows any object so that preconstructed
instances by plugins can still be bound to guice. Over time we should
add basic services as arguments to this method, but for now I have left
it empty so as to not presume what is a necessary service.
This commit is contained in:
Ryan Ernst 2016-07-11 13:07:22 -07:00
parent 47bd2f9ca5
commit 99ac65931a
13 changed files with 82 additions and 38 deletions

View File

@ -21,6 +21,8 @@ package org.elasticsearch.client.transport;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -154,11 +156,13 @@ public class TransportClient extends AbstractClient {
resourcesToClose.add(circuitBreakerService);
BigArrays bigArrays = new BigArrays(settings, circuitBreakerService);
resourcesToClose.add(bigArrays);
Collection<Object> pluginComponents = pluginsService.createComponenents();
modules.add(settingsModule);
modules.add((b -> {
b.bind(BigArrays.class).toInstance(bigArrays);
b.bind(PluginsService.class).toInstance(pluginsService);
b.bind(CircuitBreakerService.class).toInstance(circuitBreakerService);
pluginComponents.stream().forEach(p -> b.bind((Class)p.getClass()).toInstance(p));
}));
Injector injector = modules.createInjector();
@ -168,9 +172,17 @@ public class TransportClient extends AbstractClient {
final TransportProxyClient proxy = new TransportProxyClient(settings, transportService, nodesService,
actionModule.getActions().values().stream().map(x -> x.getAction()).collect(Collectors.toList()));
List<LifecycleComponent> pluginLifecycles = pluginComponents.stream()
.filter(p -> p instanceof LifecycleComponent)
.map(p -> (LifecycleComponent)p).collect(Collectors.toList());
pluginLifecycles.addAll(pluginsService.getGuiceServiceClasses().stream()
.map(injector::getInstance).collect(Collectors.toList()));
resourcesToClose.addAll(pluginLifecycles);
transportService.start();
transportService.acceptIncomingRequests();
TransportClient transportClient = new TransportClient(injector, nodesService, proxy);
TransportClient transportClient = new TransportClient(injector, pluginLifecycles, nodesService, proxy);
resourcesToClose.clear();
return transportClient;
} finally {
@ -183,12 +195,15 @@ public class TransportClient extends AbstractClient {
final Injector injector;
private final List<LifecycleComponent> pluginLifecycleComponents;
private final TransportClientNodesService nodesService;
private final TransportProxyClient proxy;
private TransportClient(Injector injector, TransportClientNodesService nodesService, TransportProxyClient proxy) {
private TransportClient(Injector injector, List<LifecycleComponent> pluginLifecycleComponents,
TransportClientNodesService nodesService, TransportProxyClient proxy) {
super(injector.getInstance(Settings.class), injector.getInstance(ThreadPool.class));
this.injector = injector;
this.pluginLifecycleComponents = Collections.unmodifiableList(pluginLifecycleComponents);
this.nodesService = nodesService;
this.proxy = proxy;
}
@ -269,8 +284,8 @@ public class TransportClient extends AbstractClient {
closeables.add(nodesService);
closeables.add(injector.getInstance(TransportService.class));
for (Class<? extends LifecycleComponent> plugin : injector.getInstance(PluginsService.class).nodeServices()) {
closeables.add(injector.getInstance(plugin));
for (LifecycleComponent plugin : pluginLifecycleComponents) {
closeables.add(plugin);
}
closeables.add(() -> ThreadPool.terminate(injector.getInstance(ThreadPool.class), 10, TimeUnit.SECONDS));
closeables.add(injector.getInstance(BigArrays.class));

View File

@ -132,6 +132,8 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A node represent a node within a cluster (<tt>cluster.name</tt>). The {@link #client()} can be used
@ -182,6 +184,7 @@ public class Node implements Closeable {
private final NodeEnvironment nodeEnvironment;
private final PluginsService pluginsService;
private final NodeClient client;
private final Collection<LifecycleComponent> pluginLifecycleComponents;
/**
* Constructs a node with the given settings.
@ -192,6 +195,7 @@ public class Node implements Closeable {
this(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), Collections.<Class<? extends Plugin>>emptyList());
}
@SuppressWarnings("unchecked")
protected Node(Environment tmpEnv, Collection<Class<? extends Plugin>> classpathPlugins) {
Settings tmpSettings = Settings.builder().put(tmpEnv.settings())
.put(Client.CLIENT_TYPE_SETTING_S.getKey(), CLIENT_TYPE).build();
@ -300,6 +304,7 @@ public class Node implements Closeable {
resourcesToClose.add(bigArrays);
modules.add(settingsModule);
client = new NodeClient(settings, threadPool);
Collection<Object> pluginComponents = pluginsService.createComponenents();
modules.add(b -> {
b.bind(PluginsService.class).toInstance(pluginsService);
b.bind(Client.class).toInstance(client);
@ -314,10 +319,19 @@ public class Node implements Closeable {
b.bind(ScriptService.class).toInstance(scriptModule.getScriptService());
b.bind(AnalysisRegistry.class).toInstance(analysisModule.getAnalysisRegistry());
b.bind(IngestService.class).toInstance(ingestService);
pluginComponents.stream().forEach(p -> b.bind((Class)p.getClass()).toInstance(p));
}
);
injector = modules.createInjector();
List<LifecycleComponent> pluginLifecycles = pluginComponents.stream()
.filter(p -> p instanceof LifecycleComponent)
.map(p -> (LifecycleComponent)p).collect(Collectors.toList());
pluginLifecycles.addAll(pluginsService.getGuiceServiceClasses().stream()
.map(injector::getInstance).collect(Collectors.toList()));
resourcesToClose.addAll(pluginLifecycles);
pluginLifecycleComponents = Collections.unmodifiableList(pluginLifecycles);
client.intialize(injector.getInstance(new Key<Map<GenericAction, TransportAction>>() {}));
success = true;
@ -373,9 +387,7 @@ public class Node implements Closeable {
logger.info("starting ...");
// hack around dependency injection problem (for now...)
injector.getInstance(Discovery.class).setAllocationService(injector.getInstance(AllocationService.class));
for (Class<? extends LifecycleComponent> plugin : pluginsService.nodeServices()) {
injector.getInstance(plugin).start();
}
pluginLifecycleComponents.forEach(LifecycleComponent::start);
injector.getInstance(MappingUpdatedAction.class).setClient(client);
injector.getInstance(IndicesService.class).start();
@ -511,9 +523,7 @@ public class Node implements Closeable {
injector.getInstance(RestController.class).stop();
injector.getInstance(TransportService.class).stop();
for (Class<? extends LifecycleComponent> plugin : pluginsService.nodeServices()) {
injector.getInstance(plugin).stop();
}
pluginLifecycleComponents.forEach(LifecycleComponent::stop);
// we should stop this last since it waits for resources to get released
// if we had scroll searchers etc or recovery going on we wait for to finish.
injector.getInstance(IndicesService.class).stop();
@ -577,9 +587,9 @@ public class Node implements Closeable {
toClose.add(() -> stopWatch.stop().start("transport"));
toClose.add(injector.getInstance(TransportService.class));
for (Class<? extends LifecycleComponent> plugin : pluginsService.nodeServices()) {
toClose.add(() -> stopWatch.stop().start("plugin(" + plugin.getName() + ")"));
toClose.add(injector.getInstance(plugin));
for (LifecycleComponent plugin : pluginLifecycleComponents) {
toClose.add(() -> stopWatch.stop().start("plugin(" + plugin.getClass().getName() + ")"));
toClose.add(plugin);
}
toClose.addAll(pluginsService.filterPlugins(Closeable.class));

View File

@ -20,6 +20,7 @@
package org.elasticsearch.plugins;
import org.elasticsearch.action.ActionModule;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.Setting;
@ -43,16 +44,28 @@ import java.util.List;
public abstract class Plugin {
/**
* Node level modules.
* Node level guice modules.
*/
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.emptyList();
}
/**
* Node level services that will be automatically started/stopped/closed.
* Node level services that will be automatically started/stopped/closed. This classes must be constructed
* by injection with guice.
*/
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
return Collections.emptyList();
}
/**
* Returns components maintained by this plugin.
*
* Any components returned that implement {@link LifecycleComponent} will have their lifecycle managed.
* Note: To aid in the migration away from guice, all objects returned as components will be bound in guice
* to themselves.
*/
public Collection<Object> createComponents() {
return Collections.emptyList();
}

View File

@ -39,13 +39,7 @@ import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.indices.analysis.AnalysisModule;
import org.elasticsearch.script.NativeScriptFactory;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.threadpool.ExecutorBuilder;
import java.io.IOException;
@ -274,10 +268,16 @@ public class PluginsService extends AbstractComponent {
return builder.put(this.settings).build();
}
@SuppressWarnings("unchecked")
public Collection<Module> nodeModules() {
List<Module> modules = new ArrayList<>();
for (Tuple<PluginInfo, Plugin> plugin : plugins) {
modules.addAll(plugin.v2().nodeModules());
modules.addAll(plugin.v2().createGuiceModules());
modules.add(b -> {
for (Object c : plugin.v2().createComponents()) {
b.bind((Class)c.getClass()).toInstance(c);
}
});
}
return modules;
}
@ -290,14 +290,20 @@ public class PluginsService extends AbstractComponent {
return builders;
}
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
/** Returns all classes injected into guice by plugins which extend {@link LifecycleComponent}. */
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
List<Class<? extends LifecycleComponent>> services = new ArrayList<>();
for (Tuple<PluginInfo, Plugin> plugin : plugins) {
services.addAll(plugin.v2().nodeServices());
services.addAll(plugin.v2().getGuiceServiceClasses());
}
return services;
}
/** Gets components from each plugin. This method should be called exactly once. */
public Collection<Object> createComponenents() {
return plugins.stream().flatMap(p -> p.v2().createComponents().stream()).collect(Collectors.toList());
}
public void onIndexModule(IndexModule indexModule) {
for (Tuple<PluginInfo, Plugin> plugin : plugins) {
plugin.v2().onIndexModule(indexModule);

View File

@ -579,7 +579,7 @@ public class ClusterServiceIT extends ESIntegTestCase {
public static class TestPlugin extends Plugin {
@Override
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
List<Class<? extends LifecycleComponent>> services = new ArrayList<>(1);
services.add(MasterAwareService.class);
return services;

View File

@ -60,7 +60,7 @@ public class SettingsListenerIT extends ESIntegTestCase {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.<Module>singletonList(new SettingsListenerModule(service));
}
}

View File

@ -51,7 +51,7 @@ public class AzureDiscoveryPlugin extends Plugin {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.singletonList((Module) new AzureDiscoveryModule(settings));
}

View File

@ -86,7 +86,7 @@ public class Ec2DiscoveryPlugin extends Plugin {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
Collection<Module> modules = new ArrayList<>();
modules.add(new Ec2Module());
return modules;
@ -94,7 +94,7 @@ public class Ec2DiscoveryPlugin extends Plugin {
@Override
@SuppressWarnings("rawtypes") // Supertype uses rawtype
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
Collection<Class<? extends LifecycleComponent>> services = new ArrayList<>();
services.add(AwsEc2ServiceImpl.class);
return services;

View File

@ -70,13 +70,13 @@ public class GceDiscoveryPlugin extends Plugin {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.singletonList(new GceModule());
}
@Override
@SuppressWarnings("rawtypes") // Supertype uses raw type
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
return Collections.singletonList(GceModule.getComputeServiceImpl());
}

View File

@ -44,13 +44,13 @@ public class JvmExamplePlugin extends Plugin {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.<Module>singletonList(new ConfiguredExampleModule());
}
@Override
@SuppressWarnings("rawtypes") // Plugin use a rawtype
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
public Collection<Class<? extends LifecycleComponent>> getGuiceServiceClasses() {
Collection<Class<? extends LifecycleComponent>> services = new ArrayList<>();
return services;
}

View File

@ -292,7 +292,7 @@ public class ContextAndHeaderTransportIT extends ESIntegTestCase {
public static class ActionLoggingPlugin extends Plugin implements ActionPlugin {
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.<Module>singletonList(new ActionLoggingModule());
}

View File

@ -49,7 +49,7 @@ public class MockEngineFactoryPlugin extends Plugin {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.singleton(new MockEngineReaderModule());
}

View File

@ -70,7 +70,7 @@ public final class MockIndexEventListener {
}
@Override
public Collection<Module> nodeModules() {
public Collection<Module> createGuiceModules() {
return Collections.singleton(binder -> binder.bind(TestEventListener.class).toInstance(listener));
}
}