Move lazy initialization classes from Watcher to XPack

This commit moves the InitializingModule and InitializingService classes in the common XPack package so that it can be used by any plugin. It also renames the module and service from Initializing* to LazyInitializing* and add a ClientProxy class.

Original commit: elastic/x-pack-elasticsearch@fbdf9d1614
This commit is contained in:
Tanguy Leroux 2016-03-08 15:02:46 +01:00
parent 0f905e9b00
commit 62ad9f4f0d
13 changed files with 139 additions and 80 deletions

View File

@ -22,6 +22,8 @@ import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.shield.Shield;
import org.elasticsearch.watcher.Watcher;
import org.elasticsearch.xpack.common.init.LazyInitializationModule;
import org.elasticsearch.xpack.common.init.LazyInitializationService;
import java.nio.file.Path;
import java.security.AccessController;
@ -89,6 +91,7 @@ public class XPackPlugin extends Plugin {
@Override
public Collection<Module> nodeModules() {
ArrayList<Module> modules = new ArrayList<>();
modules.add(new LazyInitializationModule());
modules.addAll(licensing.nodeModules());
modules.addAll(shield.nodeModules());
modules.addAll(watcher.nodeModules());
@ -99,6 +102,10 @@ public class XPackPlugin extends Plugin {
@Override
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
ArrayList<Class<? extends LifecycleComponent>> services = new ArrayList<>();
// the initialization service must be first in the list
// as other services may depend on one of the initialized
// constructs
services.add(LazyInitializationService.class);
services.addAll(licensing.nodeServices());
services.addAll(shield.nodeServices());
services.addAll(watcher.nodeServices());
@ -145,6 +152,10 @@ public class XPackPlugin extends Plugin {
shield.onIndexModule(module);
}
public void onModule(LazyInitializationModule module) {
watcher.onModule(module);
}
public static boolean transportClientMode(Settings settings) {
return !"node".equals(settings.get(Client.CLIENT_TYPE_SETTING_S.getKey()));
}

View File

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.common.init;
import org.elasticsearch.common.inject.Injector;
public interface LazyInitializable {
/**
* This method is called once all objects have been constructed and
* the @{link LazyInitializationService} has been started.
*/
void init(Injector injector);
}

View File

@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.common.init;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import java.util.HashSet;
import java.util.Set;
/**
* A module to lazy initialize objects and avoid circular dependency injection issues.
*
* Objects that use the {@link org.elasticsearch.client.ElasticsearchClient} and that are also injected in transport actions provoke
* a circular dependency injection issues with Guice. Using proxies with lazy initialization is a way to solve this issue.
*
* The proxies are initialized by {@link LazyInitializationService}.
*/
public class LazyInitializationModule extends AbstractModule {
private final Set<Class<? extends LazyInitializable>> initializables = new HashSet<>();
@Override
protected void configure() {
Multibinder<LazyInitializable> mbinder = Multibinder.newSetBinder(binder(), LazyInitializable.class);
for (Class<? extends LazyInitializable> initializable : initializables) {
bind(initializable).asEagerSingleton();
mbinder.addBinding().to(initializable);
}
bind(LazyInitializationService.class).asEagerSingleton();
}
public void registerLazyInitializable(Class<? extends LazyInitializable> lazyTypeClass) {
initializables.add(lazyTypeClass);
}
}

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.init;
package org.elasticsearch.xpack.common.init;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
@ -14,15 +14,15 @@ import org.elasticsearch.common.settings.Settings;
import java.util.Set;
/**
* A service to lazy initialize {@link InitializingService.Initializable} constructs.
* A service to lazy initialize {@link LazyInitializable} constructs.
*/
public class InitializingService extends AbstractLifecycleComponent {
public class LazyInitializationService extends AbstractLifecycleComponent {
private final Injector injector;
private final Set<Initializable> initializables;
private final Set<LazyInitializable> initializables;
@Inject
public InitializingService(Settings settings, Injector injector, Set<Initializable> initializables) {
public LazyInitializationService(Settings settings, Injector injector, Set<LazyInitializable> initializables) {
super(settings);
this.injector = injector;
this.initializables = initializables;
@ -30,7 +30,8 @@ public class InitializingService extends AbstractLifecycleComponent {
@Override
protected void doStart() throws ElasticsearchException {
for (Initializable initializable : initializables) {
for (LazyInitializable initializable : initializables) {
logger.trace("lazy initialization of [{}]", initializable);
initializable.init(injector);
}
}
@ -42,9 +43,4 @@ public class InitializingService extends AbstractLifecycleComponent {
@Override
protected void doClose() throws ElasticsearchException {
}
public interface Initializable {
void init(Injector injector);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.common.init.proxy;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.shield.InternalClient;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.common.init.LazyInitializable;
/**
* A lazily initialized proxy to an elasticsearch {@link Client}. Inject this proxy whenever a client
* needs to injected to be avoid circular dependencies issues.
*/
public class ClientProxy implements LazyInitializable {
protected InternalClient client;
@Override
public void init(Injector injector) {
this.client = injector.getInstance(InternalClient.class);
}
public AdminClient admin() {
return client.admin();
}
public void bulk(BulkRequest request, ActionListener<BulkResponse> listener) {
client.bulk(preProcess(request), listener);
}
protected <M extends TransportMessage> M preProcess(M message) {
return message;
}
}

View File

@ -38,6 +38,7 @@ import org.elasticsearch.watcher.execution.ExecutionModule;
import org.elasticsearch.watcher.history.HistoryModule;
import org.elasticsearch.watcher.history.HistoryStore;
import org.elasticsearch.watcher.input.InputModule;
import org.elasticsearch.watcher.input.chain.ChainInputFactory;
import org.elasticsearch.watcher.license.LicenseModule;
import org.elasticsearch.watcher.license.WatcherLicensee;
import org.elasticsearch.watcher.rest.action.RestAckWatchAction;
@ -54,14 +55,14 @@ import org.elasticsearch.watcher.support.WatcherIndexTemplateRegistry.TemplateCo
import org.elasticsearch.watcher.support.clock.ClockModule;
import org.elasticsearch.watcher.support.http.HttpClient;
import org.elasticsearch.watcher.support.http.HttpClientModule;
import org.elasticsearch.watcher.support.init.InitializingModule;
import org.elasticsearch.watcher.support.init.InitializingService;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.watcher.support.secret.SecretModule;
import org.elasticsearch.watcher.support.secret.SecretService;
import org.elasticsearch.watcher.support.text.TextTemplateModule;
import org.elasticsearch.watcher.support.validation.WatcherSettingsValidation;
import org.elasticsearch.watcher.transform.TransformModule;
import org.elasticsearch.watcher.transform.chain.ChainTransformFactory;
import org.elasticsearch.watcher.transport.actions.ack.AckWatchAction;
import org.elasticsearch.watcher.transport.actions.ack.TransportAckWatchAction;
import org.elasticsearch.watcher.transport.actions.activate.ActivateWatchAction;
@ -82,6 +83,7 @@ import org.elasticsearch.watcher.trigger.TriggerModule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleModule;
import org.elasticsearch.watcher.watch.WatchModule;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.common.init.LazyInitializationModule;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -133,7 +135,6 @@ public class Watcher {
}
return Arrays.<Module>asList(
new WatcherModule(settings),
new InitializingModule(),
new LicenseModule(),
new WatchModule(),
new TextTemplateModule(),
@ -156,10 +157,6 @@ public class Watcher {
return Collections.emptyList();
}
return Arrays.<Class<? extends LifecycleComponent>>asList(
// the initialization service must be first in the list
// as other services may depend on one of the initialized
// constructs
InitializingService.class,
WatcherLicensee.class,
EmailService.class,
HipChatService.class,
@ -254,6 +251,15 @@ public class Watcher {
}
}
public void onModule(LazyInitializationModule module) {
if (enabled) {
module.registerLazyInitializable(WatcherClientProxy.class);
module.registerLazyInitializable(ScriptServiceProxy.class);
module.registerLazyInitializable(ChainTransformFactory.class);
module.registerLazyInitializable(ChainInputFactory.class);
}
}
public static boolean enabled(Settings settings) {
return XPackPlugin.featureEnabled(settings, NAME, true);
}

View File

@ -48,7 +48,7 @@ public class InputModule extends AbstractModule {
bind(NoneInputFactory.class).asEagerSingleton();
parsersBinder.addBinding(NoneInput.TYPE).to(NoneInputFactory.class);
// no bind() needed, done in InitializingModule
// no bind() needed, done using the LazyInitializationModule
parsersBinder.addBinding(ChainInput.TYPE).to(ChainInputFactory.class);
for (Map.Entry<String, Class<? extends InputFactory>> entry : parsers.entrySet()) {

View File

@ -15,14 +15,14 @@ import org.elasticsearch.watcher.input.ExecutableInput;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputFactory;
import org.elasticsearch.watcher.input.InputRegistry;
import org.elasticsearch.watcher.support.init.InitializingService;
import org.elasticsearch.xpack.common.init.LazyInitializable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ChainInputFactory extends InputFactory<ChainInput, ChainInput.Result, ExecutableChainInput>
implements InitializingService.Initializable {
implements LazyInitializable {
private InputRegistry inputRegistry;

View File

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.init;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.watcher.input.chain.ChainInputFactory;
import org.elasticsearch.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.transform.chain.ChainTransformFactory;
/**
*
*/
public class InitializingModule extends AbstractModule {
@Override
protected void configure() {
bind(WatcherClientProxy.class).asEagerSingleton();
bind(ScriptServiceProxy.class).asEagerSingleton();
bind(ChainInputFactory.class).asEagerSingleton();
Multibinder<InitializingService.Initializable> mbinder = Multibinder.newSetBinder(binder(),
InitializingService.Initializable.class);
mbinder.addBinding().to(WatcherClientProxy.class);
mbinder.addBinding().to(ScriptServiceProxy.class);
mbinder.addBinding().to(ChainTransformFactory.class);
mbinder.addBinding().to(ChainInputFactory.class);
bind(InitializingService.class).asEagerSingleton();
}
}

View File

@ -13,7 +13,7 @@ import org.elasticsearch.script.ScriptService;
import org.elasticsearch.shield.SecurityContext;
import org.elasticsearch.shield.XPackUser;
import org.elasticsearch.watcher.support.Script;
import org.elasticsearch.watcher.support.init.InitializingService;
import org.elasticsearch.xpack.common.init.LazyInitializable;
import java.util.Map;
@ -23,7 +23,7 @@ import static java.util.Collections.emptyMap;
*A lazily initialized proxy to the elasticsearch {@link ScriptService}. Inject this proxy whenever the script
* service needs to be injected to avoid circular dependencies issues.
*/
public class ScriptServiceProxy implements InitializingService.Initializable {
public class ScriptServiceProxy implements LazyInitializable {
private ScriptService service;
private SecurityContext securityContext;

View File

@ -23,26 +23,22 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.shield.InternalClient;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.watcher.support.init.InitializingService;
import org.elasticsearch.xpack.common.init.proxy.ClientProxy;
/**
* A lazily initialized proxy to an elasticsearch {@link Client}. Inject this proxy whenever a client
* needs to injected to be avoid circular dependencies issues.
*/
public class WatcherClientProxy implements InitializingService.Initializable {
public class WatcherClientProxy extends ClientProxy {
private final TimeValue defaultSearchTimeout;
private final TimeValue defaultIndexTimeout;
private final TimeValue defaultBulkTimeout;
private InternalClient client;
@Inject
public WatcherClientProxy(Settings settings) {
@ -60,15 +56,6 @@ public class WatcherClientProxy implements InitializingService.Initializable {
return proxy;
}
@Override
public void init(Injector injector) {
this.client = injector.getInstance(InternalClient.class);
}
public AdminClient admin() {
return client.admin();
}
public IndexResponse index(IndexRequest request, TimeValue timeout) {
if (timeout == null) {
timeout = defaultIndexTimeout;
@ -125,8 +112,4 @@ public class WatcherClientProxy implements InitializingService.Initializable {
preProcess(request);
return client.admin().indices().putTemplate(request).actionGet(defaultIndexTimeout);
}
<M extends TransportMessage> M preProcess(M message) {
return message;
}
}

View File

@ -38,7 +38,7 @@ public class TransformModule extends AbstractModule {
bind(ScriptTransformFactory.class).asEagerSingleton();
mbinder.addBinding(ScriptTransform.TYPE).to(ScriptTransformFactory.class);
bind(ChainTransformFactory.class).asEagerSingleton();
// no bind() needed, done using the LazyInitializationModule
mbinder.addBinding(ChainTransform.TYPE).to(ChainTransformFactory.class);
for (Map.Entry<String, Class<? extends TransformFactory>> entry : factories.entrySet()) {

View File

@ -9,11 +9,11 @@ import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.support.init.InitializingService;
import org.elasticsearch.watcher.transform.ExecutableTransform;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.watcher.transform.TransformFactory;
import org.elasticsearch.watcher.transform.TransformRegistry;
import org.elasticsearch.xpack.common.init.LazyInitializable;
import java.io.IOException;
import java.util.ArrayList;
@ -22,7 +22,7 @@ import java.util.ArrayList;
*
*/
public class ChainTransformFactory extends TransformFactory<ChainTransform, ChainTransform.Result, ExecutableChainTransform> implements
InitializingService.Initializable {
LazyInitializable {
private TransformRegistry registry;