package com.netflix.governator.guice.runner.standalone;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.netflix.governator.annotations.binding.Main;
import com.netflix.governator.guice.BootstrapBinder;
import com.netflix.governator.guice.BootstrapModule;
import com.netflix.governator.guice.lazy.LazySingleton;
import com.netflix.governator.guice.lazy.LazySingletonScope;
import com.netflix.governator.guice.runner.LifecycleRunner;
import com.netflix.governator.guice.runner.TerminationEvent;
import com.netflix.governator.guice.runner.events.BlockingTerminationEvent;
import com.netflix.governator.lifecycle.LifecycleManager;
/**
* Implementation of a Runner module that should be used for runtime applications.
*
* @author elandau
*/
public class StandaloneRunnerModule implements BootstrapModule {
private static Logger LOG = LoggerFactory.getLogger(StandaloneRunnerModule.class);
/**
* This builder simplifies creation of the module in main()
*/
public static class Builder {
private List<String> args = Lists.newArrayList();
private Class<?> main;
private TerminationEvent terminateEvent;
/**
* Specify optional command line arguments to be injected. The arguments can be injected
* as
*
* <code>
* @Main List<String>
* </code>
* @param args
*/
public Builder withArgs(String[] args) {
this.args.addAll(Lists.newArrayList(args));
return this;
}
/**
* Specify an optional main class to instantiate. Alternatively the
* main class can be added as an eager singleton
* @param main
*/
public Builder withMainClass(Class<?> main) {
this.main = main;
return this;
}
/**
* Specify an externally provided {@link TerminationEvent}. If not specified
* the default {@link BlockingTerminationEvent} will be used.
* @param event
*/
public Builder withTerminateEvent(TerminationEvent event) {
this.terminateEvent = event;
return this;
}
public StandaloneRunnerModule build() {
return new StandaloneRunnerModule(this);
}
}
public static Builder builder() {
return new Builder();
}
@LazySingleton
public static class StandaloneFramework implements LifecycleRunner {
@Inject
private Injector injector ;
@Inject
private LifecycleManager manager;
@Inject(optional=true)
private @Main Class<?> mainClass;
@Inject(optional=true)
private @Main List<String> args;
@Inject
private @Main TerminationEvent terminateEvent;
/**
* This is the application's main 'run' loop. which blocks on the termination event
*/
@PostConstruct
public void init() {
try {
LOG.info("Starting application");
manager.start();
if (mainClass != null)
injector.getInstance(mainClass);
final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("GovernatorStandaloneTerminator-%d").build());
executor.execute(new Runnable() {
@Override
public void run() {
LOG.info("Waiting for terminate event");
try {
terminateEvent.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
LOG.info("Terminating application");
manager.close();
executor.shutdown();
}
});
}
catch (Exception e) {
LOG.error("Error executing application ", e);
}
}
}
private final List<String> args;
private final Class<?> main;
private final TerminationEvent terminateEvent;
public StandaloneRunnerModule(String[] args, Class<?> main) {
this.args = ImmutableList.copyOf(args);
this.main = main;
this.terminateEvent = null;
}
private StandaloneRunnerModule(Builder builder) {
this.args = builder.args;
this.main = builder.main;
this.terminateEvent = builder.terminateEvent;
}
@Singleton
public static class MainInjectorModule extends AbstractModule {
@Override
protected void configure() {
bind(LifecycleRunner.class).to(StandaloneFramework.class).asEagerSingleton();
}
}
@Override
public void configure(BootstrapBinder binder) {
binder.bind(MainInjectorModule.class);
if (main != null) {
binder.bind(main).in(LazySingletonScope.get());
binder.bind(new TypeLiteral<Class<?>>() {}).annotatedWith(Main.class).toInstance(main);
}
if (args != null) {
binder.bind(new TypeLiteral<List<String>>() {}).annotatedWith(Main.class).toInstance(args);
}
if (terminateEvent == null)
binder.bind(TerminationEvent.class).annotatedWith(Main.class).to(BlockingTerminationEvent.class);
else
binder.bind(TerminationEvent.class).annotatedWith(Main.class).toInstance(terminateEvent);
}
}