package ru.vyarus.dropwizard.guice.module.installer.feature.plugin;
import com.google.common.base.Preconditions;
import com.google.inject.Binder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import ru.vyarus.dropwizard.guice.module.installer.FeatureInstaller;
import ru.vyarus.dropwizard.guice.module.installer.install.binding.BindingInstaller;
import ru.vyarus.dropwizard.guice.module.installer.order.Order;
import ru.vyarus.dropwizard.guice.module.installer.util.FeatureUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* Shortcut for guice multibindings mechanism.
* Register beans annotated with {@link Plugin} annotation into set multibinding by base class
* defined in annotation.
* Registered set may be later injected in code as {@code Set<BaseType> plugins}.
* <p>To use {@code Map<String, BaseType>} create new annotation, annotated with {@code @Plugin}.
* Use new annotation to define plugins. It's value attribute will be used as key (this way you can use
* different enums for different plugin types and not need to always write plugin interface.</p>
*
* @author Vyacheslav Rusakov
* @since 08.10.2014
*/
@Order(80)
public class PluginInstaller implements FeatureInstaller<Object>, BindingInstaller {
private final PluginReporter reporter = new PluginReporter();
@Override
public boolean matches(final Class<?> type) {
return FeatureUtils.hasAnnotation(type, Plugin.class)
|| FeatureUtils.hasAnnotatedAnnotation(type, Plugin.class);
}
@Override
@SuppressWarnings("unchecked")
public <T> void install(final Binder binder, final Class<? extends T> type, final boolean lazy) {
Preconditions.checkArgument(!lazy, "Plugin bean can't be lazy: %s", type.getName());
final Plugin annotation = FeatureUtils.getAnnotation(type, Plugin.class);
if (annotation != null) {
final Class<T> pluginType = (Class<T>) annotation.value();
reporter.simple(pluginType, type);
Multibinder.newSetBinder(binder, pluginType).addBinding().to(type);
} else {
final Annotation namesAnnotation = FeatureUtils.getAnnotatedAnnotation(type, Plugin.class);
final Method valueMethod = FeatureUtils.findMethod(namesAnnotation.annotationType(), "value");
final Class<Object> keyType = (Class<Object>) valueMethod.getReturnType();
final Object key = FeatureUtils.invokeMethod(valueMethod, namesAnnotation);
final Plugin pluginAnnotation = namesAnnotation.annotationType().getAnnotation(Plugin.class);
final Class<Object> pluginType = (Class<Object>) pluginAnnotation.value();
reporter.named(keyType, pluginType, key, type);
registerNamedPlugin(binder, pluginType, keyType, type, key);
}
}
private <T, K> void registerNamedPlugin(final Binder binder, final Class<T> pluginType, final Class<K> keyType,
final Class<? extends T> plugin, final K key) {
MapBinder.newMapBinder(binder, keyType, pluginType).addBinding(key).to(plugin);
}
@Override
public void report() {
reporter.report();
}
}