/* * Copyright (C) 2015 SoftIndex LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.datakernel.launcher; import com.google.inject.*; import io.datakernel.config.PropertiesConfigModule; import io.datakernel.jmx.JmxRegistrator; import io.datakernel.service.ServiceGraph; import io.datakernel.service.ServiceGraphModule; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.slf4j.LoggerFactory.getLogger; /** * Integrates all modules together and manages application lifecycle by * passing several steps: * <ul> * <li>wiring modules</li> * <li>starting services</li> * <li>running</li> * <li>stopping services</li> * </ul> * <p> * Example.<br> * Prerequisites: an application consists of three modules, which preferably * should be configured using separate configs and may depend on each other. * <pre><code> * public class ApplicationLauncher extends Launcher { * * public ApplicationLauncher() { * super({@link Stage Stage.PRODUCTION}, {@link ServiceGraphModule}.defaultInstance(), * new DaoTierModule(), * new ControllerTierModule(), * new ViewTierModule(), * {@link PropertiesConfigModule} * .{@link PropertiesConfigModule#ofFile(String) ofFile(dao.properties)} * .{@link PropertiesConfigModule#addFile(String) addFile(controller.properties)} * .{@link PropertiesConfigModule#addOptionalFile(String) addOptionalFile(view.properties)}); * } * * {@literal @}Override * protected void run() throws Exception { * awaitShutdown(); * } * * public static void main(String[] args) throws Exception { * main(ApplicationLauncher.class, args); * } * } * </code></pre> * * @see ServiceGraph * @see ServiceGraphModule * @see PropertiesConfigModule * @see PropertiesConfigModule#ofFile(String) * @see PropertiesConfigModule#addFile(String) * @see PropertiesConfigModule#addOptionalFile(String) */ public abstract class Launcher { protected final Logger logger = getLogger(this.getClass()); protected String[] args; private JmxRegistrator jmxRegistrator; private Stage stage; private Module[] modules; @Inject protected Provider<ServiceGraph> serviceGraphProvider; @Inject protected ShutdownNotification shutdownNotification; private final Thread mainThread = Thread.currentThread(); public static <T extends Launcher> void main(Class<T> launcherClass, String[] args) throws Exception { T launcher = launcherClass.newInstance(); launcher.launch(args); } public Launcher(Stage stage, Module... modules) { this.stage = stage; this.modules = modules; } public List<Module> getModules() { List<Module> moduleList = new ArrayList<>(Arrays.asList(this.modules)); moduleList.add(new AbstractModule() { @Override protected void configure() { bind(String[].class).annotatedWith(Args.class).toInstance(args != null ? args : new String[]{}); } }); return moduleList; } public final Injector testInjector() { List<Module> modules = getModules(); modules.add(new AbstractModule() { @Override protected void configure() { bind((Class<?>) Launcher.this.getClass()); } }); return Guice.createInjector(Stage.TOOL, modules); } public void launch(String[] args) throws Exception { this.args = args; Injector injector = Guice.createInjector(stage, getModules()); logger.info("=== INJECTING DEPENDENCIES"); doInject(injector); try { onStart(); try { logger.info("=== STARTING APPLICATION"); doStart(); logger.info("=== RUNNING APPLICATION"); run(); } finally { logger.info("=== STOPPING APPLICATION"); doStop(); } } catch (Exception e) { logger.error("Application failure", e); throw e; } finally { onStop(); } } private void doInject(Injector injector) throws Exception { injector.injectMembers(this); Binding<JmxRegistrator> binding = injector.getExistingBinding(Key.get(JmxRegistrator.class)); if (binding != null) { jmxRegistrator = binding.getProvider().get(); } } private void doStart() throws Exception { if (jmxRegistrator != null) { jmxRegistrator.registerJmxMBeans(); } else { logger.info("Jmx is disabled. Add JmxModule to enable."); } serviceGraphProvider.get().startFuture().get(); } protected void onStart() throws Exception { } protected abstract void run() throws Exception; protected void onStop() throws Exception { } private void doStop() throws Exception { serviceGraphProvider.get().stopFuture().get(); } protected final void awaitShutdown() throws InterruptedException { addShutdownHook(); shutdownNotification.await(); } protected final void requestShutdown() { shutdownNotification.requestShutdown(); } private void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread("shutdownNotification") { @Override public void run() { try { shutdownNotification.requestShutdown(); mainThread.join(); } catch (InterruptedException e) { logger.error("Failed shutdown", e); } } }); } }