package com.netflix.governator; import java.util.Arrays; import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Stage; import com.google.inject.multibindings.Multibinder; import com.netflix.governator.annotations.SuppressLifecycleUninitialized; import com.netflix.governator.annotations.binding.Arguments; import com.netflix.governator.annotations.binding.Profiles; import com.netflix.governator.internal.DefaultPropertySource; import com.netflix.governator.internal.GovernatorFeatureSet; import com.netflix.governator.spi.InjectorCreator; import com.netflix.governator.spi.LifecycleListener; import com.netflix.governator.spi.PropertySource; /** * Custom strategy for creating a Guice Injector that enables support for lifecycle annotations such * as {@link @PreDestroy} and {@link @PostConstruct} as well as injector lifecycle hooks via the * {@link LifecycleListener} API. * * The LifecycleInjectorCreator may be overridden to handle pre-create and post-create notification. */ public class LifecycleInjectorCreator implements InjectorCreator<LifecycleInjector> { private static final Logger LOG = LoggerFactory.getLogger(LifecycleInjectorCreator.class); private String[] args = new String[]{}; private LinkedHashSet<String> profiles = new LinkedHashSet<>(); private IdentityHashMap<GovernatorFeature<?>, Object> features = new IdentityHashMap<>(); public LifecycleInjectorCreator withArguments(String[] args) { Preconditions.checkArgument(args != null, "Arg may not be null"); this.args = args; return this; } public LifecycleInjectorCreator withProfiles(String... profiles) { Preconditions.checkArgument(profiles != null, "Arg may not be null"); this.profiles = new LinkedHashSet<>(Arrays.asList(profiles)); return this; } public LifecycleInjectorCreator withProfiles(Set<String> profiles) { Preconditions.checkArgument(profiles != null, "profiles may not be null"); this.profiles = new LinkedHashSet<>(profiles); return this; } public LifecycleInjectorCreator withFeatures(IdentityHashMap<GovernatorFeature<?>, Object> features) { Preconditions.checkArgument(features != null, "features may not be null"); this.features = features; return this; } @Singleton @SuppressLifecycleUninitialized class GovernatorFeatureSetImpl implements GovernatorFeatureSet { private final IdentityHashMap<GovernatorFeature<?>, Object> featureOverrides; @Inject private PropertySource properties = new DefaultPropertySource(); @Inject public GovernatorFeatureSetImpl(IdentityHashMap<GovernatorFeature<?>, Object> featureOverrides) { this.featureOverrides = featureOverrides; } @SuppressWarnings("unchecked") @Override public <T> T get(GovernatorFeature<T> feature) { return featureOverrides.containsKey(feature) ? (T) featureOverrides.get(feature) : (T) properties.get(feature.getKey(), feature.getType(), feature.getDefaultValue()); } } @Override public LifecycleInjector createInjector(Stage stage, Module module) { final GovernatorFeatureSetImpl featureSet = new GovernatorFeatureSetImpl(features); final LifecycleManager manager = new LifecycleManager(); // Construct the injector using our override structure try { onBeforeInjectorCreate(); Injector injector = Guice.createInjector( stage, // This has to be first to make sure @PostConstruct support is added as early // as possible new ProvisionMetricsModule(), new LifecycleModule(), new LifecycleListenerModule(), new LegacyScopesModule(), new AbstractModule() { @Override protected void configure() { bind(GovernatorFeatureSet.class).toInstance(featureSet); bind(LifecycleManager.class).toInstance(manager); Multibinder<String> profilesBinder = Multibinder.newSetBinder(binder(), Key.get(String.class, Profiles.class)).permitDuplicates(); profiles.forEach(profile -> profilesBinder.addBinding().toInstance(profile)); bind(String[].class).annotatedWith(Arguments.class).toInstance(args); requestInjection(LifecycleInjectorCreator.this); } }, module ); manager.notifyStarted(); LifecycleInjector lifecycleInjector = LifecycleInjector.wrapInjector(injector, manager); onSuccessfulInjectorCreate(); return lifecycleInjector; } catch (Exception e) { LOG.error("Failed to create injector - {}@{}", e.getClass().getSimpleName(), System.identityHashCode(e), e); onFailedInjectorCreate(e); try { manager.notifyStartFailed(e); } catch (Exception e2) { LOG.error("Failed to notify injector creation failure", e2 ); } if (!featureSet.get(GovernatorFeatures.SHUTDOWN_ON_ERROR)) { return LifecycleInjector.createFailedInjector(manager); } else { throw e; } } finally { onCompletedInjectorCreate(); } } /** * Template method invoked immediately before the injector is created */ protected void onBeforeInjectorCreate() { } /** * Template method invoked immediately after the injector is created */ protected void onSuccessfulInjectorCreate() { } /** * Template method invoked immediately after any failure to create the injector * @param error Cause of the failure */ protected void onFailedInjectorCreate(Throwable error) { } /** * Template method invoked at the end of createInjector() regardless of whether * the injector was created successful or not. */ protected void onCompletedInjectorCreate() { } @Override public String toString() { return "LifecycleInjectorCreator[]"; } }