Plugins: Allow for plugins to implement onModule method that will be automatically injected with the relevant module type, closes #1613.

This commit is contained in:
Shay Banon 2012-01-15 16:42:29 +02:00
parent e37c0904f0
commit 1d35e27b3b
3 changed files with 63 additions and 4 deletions

View File

@ -30,8 +30,10 @@ import java.util.Collection;
/** /**
* A base class for a plugin. * A base class for a plugin.
* * <p/>
* * A plugin can be dynamically injected with {@link Module} by implementing <tt>onModule(AnyModule)</tt> method
* removing the need to override {@link #processModule(org.elasticsearch.common.inject.Module)} and check using
* instanceof.
*/ */
public abstract class AbstractPlugin implements Plugin { public abstract class AbstractPlugin implements Plugin {

View File

@ -28,8 +28,10 @@ import java.util.Collection;
/** /**
* An extension point allowing to plug in custom functionality. * An extension point allowing to plug in custom functionality.
* * <p/>
* * A plugin can be dynamically injected with {@link Module} by implementing <tt>onModule(AnyModule)</tt> method
* removing the need to override {@link #processModule(org.elasticsearch.common.inject.Module)} and check using
* instanceof.
*/ */
public interface Plugin { public interface Plugin {
@ -73,6 +75,10 @@ public interface Plugin {
*/ */
Collection<Class<? extends CloseableIndexComponent>> shardServices(); Collection<Class<? extends CloseableIndexComponent>> shardServices();
/**
* Process a specific module. Note, its simpler to implement a custom <tt>onModule(AnyModule module)</tt>
* method, which will be automatically be called by the relevant type.
*/
void processModule(Module module); void processModule(Module module);
/** /**

View File

@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
@ -51,6 +52,18 @@ public class PluginsService extends AbstractComponent {
private final ImmutableMap<String, Plugin> plugins; private final ImmutableMap<String, Plugin> plugins;
private final ImmutableMap<Plugin, List<OnModuleReference>> onModuleReferences;
static class OnModuleReference {
public final Class<? extends Module> moduleClass;
public final Method onModuleMethod;
OnModuleReference(Class<? extends Module> moduleClass, Method onModuleMethod) {
this.moduleClass = moduleClass;
this.onModuleMethod = onModuleMethod;
}
}
@Inject @Inject
public PluginsService(Settings settings, Environment environment) { public PluginsService(Settings settings, Environment environment) {
super(settings); super(settings);
@ -65,6 +78,31 @@ public class PluginsService extends AbstractComponent {
logger.info("loaded {}, sites {}", plugins.keySet(), sitePlugins()); logger.info("loaded {}, sites {}", plugins.keySet(), sitePlugins());
this.plugins = ImmutableMap.copyOf(plugins); this.plugins = ImmutableMap.copyOf(plugins);
MapBuilder<Plugin, List<OnModuleReference>> onModuleReferences = MapBuilder.newMapBuilder();
for (Plugin plugin : plugins.values()) {
List<OnModuleReference> list = Lists.newArrayList();
for (Method method : plugin.getClass().getDeclaredMethods()) {
if (!method.getName().equals("onModule")) {
continue;
}
if (method.getParameterTypes().length == 0 || method.getParameterTypes().length > 1) {
logger.warn("Plugin: {} implementing onModule with no parameters or more than one parameter", plugin.name());
continue;
}
Class moduleClass = method.getParameterTypes()[0];
if (!Module.class.isAssignableFrom(moduleClass)) {
logger.warn("Plugin: {} implementing onModule by the type is not of Module type {}", plugin.name(), moduleClass);
continue;
}
method.setAccessible(true);
list.add(new OnModuleReference(moduleClass, method));
}
if (!list.isEmpty()) {
onModuleReferences.put(plugin, list);
}
}
this.onModuleReferences = onModuleReferences.immutableMap();
} }
public ImmutableMap<String, Plugin> plugins() { public ImmutableMap<String, Plugin> plugins() {
@ -80,6 +118,19 @@ public class PluginsService extends AbstractComponent {
public void processModule(Module module) { public void processModule(Module module) {
for (Plugin plugin : plugins().values()) { for (Plugin plugin : plugins().values()) {
plugin.processModule(module); plugin.processModule(module);
// see if there are onModule references
List<OnModuleReference> references = onModuleReferences.get(plugin);
if (references != null) {
for (OnModuleReference reference : references) {
if (reference.moduleClass.isAssignableFrom(module.getClass())) {
try {
reference.onModuleMethod.invoke(plugin, module);
} catch (Exception e) {
logger.warn("plugin {}, failed to invoke custom onModule method", e, plugin.name());
}
}
}
}
} }
} }