/** This file is part of Waarp Project. Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the COPYRIGHT.txt in the distribution for a full listing of individual contributors. All Waarp Project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Waarp . If not, see <http://www.gnu.org/licenses/>. */ package org.waarp.common.service; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.commons.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; import org.apache.commons.daemon.DaemonController; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.common.logging.WaarpSlf4JLoggerFactory; import org.waarp.common.utility.WaarpThreadFactory; /** * Launch the Engine from a variety of sources, either through a main() or invoked through * Apache Daemon. * * @author Frederic Bregier * Inspired from Apache Daemon Wiki * */ public abstract class ServiceLauncher implements Daemon { /** * Internal Logger */ protected static WaarpLogger logger; protected static EngineAbstract engine = null; protected static ServiceLauncher engineLauncherInstance = null; protected ExecutorService executor = null; protected static DaemonController controller = null; protected static boolean stopCalledCorrectly = false; /** * * @return a new EngineAbstract */ protected abstract EngineAbstract getNewEngineAbstract(); public ServiceLauncher() { if (logger == null) { logger = WaarpLoggerFactory.getLogger(ServiceLauncher.class); } if (executor == null) { executor = Executors.newSingleThreadExecutor(new WaarpThreadFactory("ServiceLauncher")); } engineLauncherInstance = this; if (engine == null) { engine = getNewEngineAbstract(); } } protected static void initStatic() { if (!(WaarpLoggerFactory.getDefaultFactory() instanceof WaarpSlf4JLoggerFactory)) { WaarpLoggerFactory.setDefaultFactory(new WaarpSlf4JLoggerFactory(null)); } if (logger == null) { logger = WaarpLoggerFactory.getLogger(ServiceLauncher.class); } String className = Thread.currentThread().getStackTrace()[3].getClassName(); ; logger.debug("Engine " + className); try { engineLauncherInstance = (ServiceLauncher) Class.forName(className).newInstance(); } catch (Throwable e) { logger.error("Engine not correctly initialized", e); System.exit(2); } if (engineLauncherInstance == null || engine == null) { logger.error("Engine not correctly initialized"); System.exit(1); } } /** * The Java entry point. * * @param args * Command line arguments, all ignored. */ public static void _main(String[] args) { initStatic(); // the main routine is only here so I can also run the app from the command line engineLauncherInstance.initialize(); Scanner sc = new Scanner(System.in); // wait until receive stop command from keyboard System.out.printf("Enter 'stop' to halt: "); while (!sc.nextLine().toLowerCase().equals("stop")) ; if (!engine.isShutdown()) { engineLauncherInstance.terminate(); } sc.close(); } /** * Windows mode<br> * <br> * Static methods called by prunsrv to start/stop * the Windows service. Pass the argument "start" * to start the service, and pass "stop" to * stop the service. * * <pre> * prunsrv.exe //IS/MyService --Classpath=C:\...\xxx.jar --Description="My Java Service" --Jvm=auto --StartMode=jvm --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsService --StartParams=start --StopMode=jvm --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsService --StopParams=stop * </pre> * * @param args * Arguments from prunsrv command line * @throws Exception **/ public static void _windowsService(String args[]) throws Exception { initStatic(); String cmd = "start"; if (args.length > 0) { cmd = args[0]; } if ("start".equals(cmd)) { engineLauncherInstance.windowsStart(); } else { engineLauncherInstance.windowsStop(); } } /** * Windows mode<br> * <br> * Static methods called by prunsrv to start * the Windows service. * * <pre> * prunsrv.exe //IS/MyService --Classpath=C:\...\xxx.jar --Description="My Java Service" --Jvm=auto --StartMode=jvm --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsStart --StopMode=jvm --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsStop * </pre> * * @param args * Arguments are ignored * @throws Exception **/ public static void _windowsStart(String args[]) throws Exception { initStatic(); engineLauncherInstance.windowsStart(); } /** * Windows mode<br> * <br> * Static methods called by prunsrv to stop * the Windows service. * * <pre> * prunsrv.exe //IS/MyService --Classpath=C:\...\xxx.jar --Description="My Java Service" --Jvm=auto --StartMode=jvm --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsStart --StopMode=jvm --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsStop * </pre> * * @param args * Arguments are ignored **/ public static void _windowsStop(String args[]) { initStatic(); stopCalledCorrectly = true; engineLauncherInstance.windowsStop(); } /** * Internal command * * @throws Exception */ protected void windowsStart() throws Exception { logger.info("windowsStart called"); initialize(); // We must wait in Windows Mode boolean status = false; try { status = engine.waitShutdown(); } catch (InterruptedException e) { } if (!status || !stopCalledCorrectly) { // Was stopped outside service management terminate(); if (controller != null) { controller.fail("Service stopped abnormally"); } else { throw new Exception("Service stopped abnormally"); } } } /** * Internal command */ protected void windowsStop() { logger.info("windowsStop called from Service: " + stopCalledCorrectly); terminate(); // should we force Future to be cancelled there? } // Implementing the Daemon interface is not required for Windows but is for Linux public void init(DaemonContext arg0) throws Exception { controller = arg0.getController(); logger.info("Daemon init"); } public void start() { logger.info("Daemon start"); initialize(); } public void stop() { logger.info("Daemon stop"); terminate(); } public void destroy() { logger.info("Daemon destroy"); terminate(); } /** * Do the work of starting the engine */ protected void initialize() { if (engine != null) { logger.info("Starting the Engine"); engine.setDaemon(true); executor.execute(engine); } else { logger.error("Engine cannot be started since it is not initialized"); } } /** * Cleanly stop the engine. */ protected void terminate() { if (engine != null) { logger.info("Stopping the Engine"); engine.shutdown(); engine = null; } if (executor != null) { executor.shutdown(); executor = null; } logger.info("Engine stopped"); } }