View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.service;
21  
22  import org.apache.commons.daemon.Daemon;
23  import org.apache.commons.daemon.DaemonContext;
24  import org.apache.commons.daemon.DaemonController;
25  import org.waarp.common.exception.InvalidArgumentException;
26  import org.waarp.common.logging.SysErrLogger;
27  import org.waarp.common.logging.WaarpLogger;
28  import org.waarp.common.logging.WaarpLoggerFactory;
29  import org.waarp.common.logging.WaarpSlf4JLoggerFactory;
30  import org.waarp.common.utility.WaarpSystemUtil;
31  import org.waarp.common.utility.WaarpThreadFactory;
32  
33  import java.util.Scanner;
34  import java.util.concurrent.ExecutorService;
35  import java.util.concurrent.Executors;
36  
37  /**
38   * Launch the Engine from a variety of sources, either through a main() or
39   * invoked through Apache Daemon.
40   *
41   * Inspired from Apache Daemon Wiki
42   */
43  public abstract class ServiceLauncher implements Daemon {
44    /**
45     * Internal Logger
46     */
47    protected static WaarpLogger logger;
48  
49    protected static EngineAbstract engine;
50  
51    protected static ServiceLauncher engineLauncherInstance;
52  
53    protected ExecutorService executor;
54  
55    protected static DaemonController controller;
56  
57    protected static boolean stopCalledCorrectly;
58  
59    /**
60     * @return a new EngineAbstract
61     */
62    protected abstract EngineAbstract getNewEngineAbstract();
63  
64    protected ServiceLauncher() {
65      if (logger == null) {
66        logger = WaarpLoggerFactory.getLogger(ServiceLauncher.class);
67      }
68      if (executor == null) {
69        executor = Executors.newSingleThreadExecutor(
70            new WaarpThreadFactory("ServiceLauncher"));
71      }
72      engineLauncherInstance = this;
73      if (engine == null) {
74        engine = getNewEngineAbstract();
75      }
76    }
77  
78    protected static void initStatic() {
79      WaarpLoggerFactory.setDefaultFactoryIfNotSame(
80          new WaarpSlf4JLoggerFactory(null));
81      if (logger == null) {
82        logger = WaarpLoggerFactory.getLogger(ServiceLauncher.class);
83      }
84      final String className =
85          Thread.currentThread().getStackTrace()[3].getClassName();
86  
87      logger.debug("Engine {}", className);
88      try {
89        engineLauncherInstance =
90            (ServiceLauncher) WaarpSystemUtil.newInstance(className);//NOSONAR
91      } catch (final Throwable e) {
92        logger.error("Engine not correctly initialized", e);
93        System.exit(2);//NOSONAR
94      }
95      if (engineLauncherInstance == null || engine == null) {
96        logger.error("Engine not correctly initialized");
97        System.exit(1);//NOSONAR
98      }
99    }
100 
101   /**
102    * The Java entry point.
103    *
104    * @param args Command line arguments, all ignored.
105    */
106   public static void _main(final String[] args) {
107     initStatic();
108     // the main routine is only here so I can also run the app from the command line
109     engineLauncherInstance.initialize();
110 
111     final Scanner sc = new Scanner(System.in);
112     // wait until receive stop command from keyboard
113     SysErrLogger.FAKE_LOGGER.sysout("Enter 'stop' to halt: ");
114     while (!"stop".equalsIgnoreCase(sc.nextLine())) {
115       // nothing
116     }
117 
118     if (!engine.isShutdown()) {
119       engineLauncherInstance.terminate();
120     }
121     sc.close();
122   }
123 
124   /**
125    * Windows mode<br>
126    * <br>
127    * Static methods called by prunsrv to start/stop the Windows service. Pass
128    * the argument "start" to start the
129    * service, and pass "stop" to stop the service.
130    *
131    * <pre>
132    * prunsrv.exe // IS/MyService --Classpath=C:\...\xxx.jar --Description=&quot;My Java Service&quot; --Jvm=auto --StartMode=jvm
133    *             // --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsService --StartParams=start
134    *             // --StopMode=jvm --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsService
135    *             // --StopParams=stop
136    * </pre>
137    *
138    * @param args Arguments from prunsrv command line
139    *
140    * @throws Exception
141    **/
142   public static void _windowsService(final String[] args) throws Exception {
143     initStatic();
144     String cmd = "start";
145     if (args.length > 0) {
146       cmd = args[0];
147     }
148     if ("start".equals(cmd)) {
149       engineLauncherInstance.windowsStart();
150     } else {
151       engineLauncherInstance.windowsStop();
152     }
153   }
154 
155   /**
156    * Windows mode<br>
157    * <br>
158    * Static methods called by prunsrv to start the Windows service.
159    *
160    * <pre>
161    * prunsrv.exe // IS/MyService --Classpath=C:\...\xxx.jar --Description=&quot;My Java Service&quot; --Jvm=auto --StartMode=jvm
162    *             // --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsStart --StopMode=jvm
163    *             // --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsStop
164    * </pre>
165    *
166    * @param args Arguments are ignored
167    *
168    * @throws Exception
169    **/
170   public static void _windowsStart(final String[] args) throws Exception {
171     initStatic();
172     engineLauncherInstance.windowsStart();
173   }
174 
175   /**
176    * Windows mode<br>
177    * <br>
178    * Static methods called by prunsrv to stop the Windows service.
179    *
180    * <pre>
181    * prunsrv.exe // IS/MyService --Classpath=C:\...\xxx.jar --Description=&quot;My Java Service&quot; --Jvm=auto --StartMode=jvm
182    *             // --StartClass=org.waarp.xxx.service.ServiceLauncher --StartMethod=windowsStart --StopMode=jvm
183    *             // --StopClass=org.waarp.xxx.service.ServiceLauncher --StopMethod=windowsStop
184    * </pre>
185    *
186    * @param args Arguments are ignored
187    **/
188   public static void _windowsStop(final String[] args) {
189     initStatic();
190     stopCalledCorrectly = true;
191     engineLauncherInstance.windowsStop();
192   }
193 
194   /**
195    * Internal command
196    *
197    * @throws Exception
198    */
199   protected void windowsStart() throws Exception {
200     logger.info("windowsStart called");
201     initialize();
202     // We must wait in Windows Mode
203     boolean status = false;
204     try {
205       status = engine.waitShutdown();
206     } catch (final InterruptedException e) {//NOSONAR
207       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
208     }
209     if (!status || !stopCalledCorrectly) {
210       // Was stopped outside service management
211       terminate();
212       if (controller != null) {
213         controller.fail("Service stopped abnormally");
214       } else {
215         throw new InvalidArgumentException("Service stopped abnormally");
216       }
217     }
218   }
219 
220   /**
221    * Internal command
222    */
223   protected void windowsStop() {
224     logger.info("windowsStop called from Service: {}", stopCalledCorrectly);
225     terminate();
226     // should we force Future to be cancelled there?
227   }
228 
229   // Implementing the Daemon interface is not required for Windows but is for Linux
230   @Override
231   public void init(final DaemonContext arg0) {
232     controller = arg0.getController();
233     logger.info("Daemon init");
234   }
235 
236   @Override
237   public void start() {
238     logger.info("Daemon start");
239     initialize();
240   }
241 
242   @Override
243   public void stop() {
244     logger.info("Daemon stop");
245     terminate();
246   }
247 
248   @Override
249   public void destroy() {
250     logger.info("Daemon destroy");
251     terminate();
252   }
253 
254   /**
255    * Do the work of starting the engine
256    */
257   protected void initialize() {
258     if (engine != null) {
259       logger.info("Starting the Engine");
260       engine.setDaemon(true);
261       executor.execute(engine);
262     } else {
263       logger.error("Engine cannot be started since it is not initialized");
264     }
265   }
266 
267   /**
268    * Cleanly stop the engine.
269    */
270   protected void terminate() {
271     if (engine != null) {
272       logger.info("Stopping the Engine");
273       engine.shutdown();
274       engine = null;
275     }
276     if (executor != null) {
277       executor.shutdown();
278       executor = null;
279     }
280     logger.info("Engine stopped");
281   }
282 }