1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.common.utility;
21
22 import org.waarp.common.future.WaarpFuture;
23 import org.waarp.common.logging.SysErrLogger;
24 import org.waarp.common.logging.WaarpLogger;
25 import org.waarp.common.logging.WaarpLoggerFactory;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.lang.management.ManagementFactory;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.Timer;
34 import java.util.TimerTask;
35
36
37
38
39 public abstract class WaarpShutdownHook extends Thread {
40
41
42
43 private static final WaarpLogger logger =
44 WaarpLoggerFactory.getLogger(WaarpShutdownHook.class);
45
46
47
48
49
50 private static final String SUN_JAVA_COMMAND = "sun.java.command";
51
52
53
54 public static WaarpShutdownHook shutdownHook;
55
56
57
58 private static volatile boolean shutdown;
59
60
61
62 private static volatile boolean shutdownStarted;
63
64
65
66 private static volatile boolean immediate;
67
68
69
70 private static boolean initialized;
71
72
73
74 private static boolean isShutdownOver;
75 private static String applArgs;
76 private static volatile boolean shouldRestart;
77 private ShutdownConfiguration shutdownConfiguration;
78
79 protected WaarpShutdownHook(final ShutdownConfiguration configuration) {
80 if (initialized) {
81 shutdownHook.shutdownConfiguration = configuration;
82 setName("WaarpShutdownHook");
83 setDaemon(true);
84 shutdownHook = this;
85 shutdownConfiguration = configuration;
86 return;
87 }
88 shutdownConfiguration = configuration;
89 setName("WaarpShutdownHook");
90 setDaemon(true);
91 shutdownHook = this;
92 initialized = true;
93 }
94
95
96
97
98 public final ShutdownConfiguration getShutdownConfiguration() {
99 return shutdownConfiguration;
100 }
101
102
103
104
105 public static void addShutdownHook() {
106 if (shutdownHook != null) {
107 Runtime.getRuntime().addShutdownHook(shutdownHook);
108 }
109 }
110
111
112
113
114
115
116 public static boolean isInShutdown() {
117 return shutdown;
118 }
119
120
121
122
123 public static boolean isShutdownStarting() {
124 return shutdownStarted;
125 }
126
127
128
129
130 public static void shutdownWillStart() {
131 shutdownStarted = true;
132 }
133
134
135
136
137
138
139
140 public static void terminate(final boolean immediateSet) {
141 if (immediateSet) {
142 immediate = true;
143 }
144 if (shutdownHook != null) {
145 removeShutdownHook();
146 terminate();
147 shutdownHook = null;
148 } else {
149 logger.error("No ShutdownHook setup");
150
151 }
152 }
153
154
155
156
157 public static void removeShutdownHook() {
158 if (shutdownHook != null) {
159 Runtime.getRuntime().removeShutdownHook(shutdownHook);
160 }
161 }
162
163
164
165
166 private static void terminate() {
167 shutdownStarted = true;
168 shutdown = true;
169 if (isShutdownOver || shutdownHook == null) {
170 shutdown = false;
171 shutdownStarted = false;
172 isShutdownOver = false;
173 initialized = false;
174 return;
175 }
176 if (immediate) {
177 shutdownHook.exitService();
178
179 try {
180 Thread.sleep(shutdownHook.shutdownConfiguration.timeout / 2);
181 } catch (final InterruptedException e) {
182 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
183 }
184 if (logger.isDebugEnabled()) {
185 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
186 for (final Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
187 printStackTrace(entry.getKey(), entry.getValue());
188 }
189 }
190 isShutdownOver = true;
191 logger.info("Should restart? {}", isRestart());
192 try {
193 restartApplication();
194 } catch (final IOException e1) {
195 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
196 }
197 shutdownHook.serviceStopped();
198 SysErrLogger.FAKE_LOGGER.syserr("Halt System");
199 try {
200 Thread.sleep(WaarpNettyUtil.SIMPLE_DELAY_MS);
201 } catch (final InterruptedException e) {
202 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
203 }
204
205 } else {
206 shutdownHook.launchFinalExit();
207 immediate = true;
208 shutdownHook.exitService();
209 isShutdownOver = true;
210 shutdownHook.serviceStopped();
211 logger.info("Should restart? {}", isRestart());
212 try {
213 restartApplication();
214 } catch (final IOException e1) {
215 SysErrLogger.FAKE_LOGGER.syserr(e1);
216 }
217 logger.info("Exit System");
218 SysErrLogger.FAKE_LOGGER.syserr("Exit System");
219 }
220 shutdown = false;
221 shutdownStarted = false;
222 isShutdownOver = false;
223 initialized = false;
224 }
225
226
227
228
229 protected abstract void exitService();
230
231
232
233
234
235
236
237 private static void printStackTrace(final Thread thread,
238 final StackTraceElement[] stacks) {
239 SysErrLogger.FAKE_LOGGER.syserrNoLn(thread + " : ");
240 for (int i = 0; i < stacks.length - 1; i++) {
241 SysErrLogger.FAKE_LOGGER.syserrNoLn(stacks[i] + " ");
242 }
243 if (stacks.length >= 1) {
244 SysErrLogger.FAKE_LOGGER.syserr(stacks[stacks.length - 1]);
245 } else {
246 SysErrLogger.FAKE_LOGGER.syserr();
247 }
248 }
249
250
251
252
253 public static boolean isRestart() {
254 return shouldRestart;
255 }
256
257
258
259
260
261
262
263
264
265
266 private static void restartApplication() throws IOException {
267 if (shouldRestart) {
268 try {
269
270 final String java = System.getProperty("java.home") + "/bin/java";
271
272 final List<String> vmArguments =
273 ManagementFactory.getRuntimeMXBean().getInputArguments();
274 final StringBuilder vmArgsOneLine = new StringBuilder();
275 for (final String arg : vmArguments) {
276
277
278 if (!arg.contains("-agentlib")) {
279 vmArgsOneLine.append(arg).append(' ');
280 }
281 }
282
283 final StringBuilder cmd;
284 if (DetectionUtils.isWindows()) {
285 cmd = new StringBuilder('"' + java + "\" " + vmArgsOneLine);
286 } else {
287 cmd = new StringBuilder(java + ' ' + vmArgsOneLine);
288 }
289
290 if (applArgs == null) {
291 applArgs = getArgs();
292 }
293 if (applArgs == null) {
294
295 SysErrLogger.FAKE_LOGGER.syserr("Cannot restart!");
296
297 throw new IOException(
298 "Error while trying to restart the " + "application");
299 }
300 cmd.append(applArgs);
301 logger.debug("Should restart with:\n{}", cmd);
302 logger.warn("Should restart");
303 Runtime.getRuntime().exec(cmd.toString());
304 } catch (final Throwable e) {
305
306 throw new IOException("Error while trying to restart the application",
307 e);
308 }
309 }
310 }
311
312 private boolean serviceStopped() {
313 if (shutdownConfiguration.serviceFuture != null) {
314 logger.info("Service will be stopped");
315 shutdownConfiguration.serviceFuture.setSuccess();
316 return true;
317 }
318 return false;
319 }
320
321
322
323
324 public final void launchFinalExit() {
325 if (WaarpSystemUtil.isJunit()) {
326 return;
327 }
328 final Timer timer = new Timer("WaarpFinalExit", true);
329 final ShutdownTimerTask timerTask = new ShutdownTimerTask();
330 timer.schedule(timerTask, shutdownConfiguration.timeout * 4);
331 }
332
333
334
335
336
337
338 private static String getArgs() {
339 final String test = System.getProperty(SUN_JAVA_COMMAND);
340 if (ParametersChecker.isNotEmpty(test)) {
341
342
343 final String[] mainCommand = test.split(" ");
344
345 final StringBuilder args = new StringBuilder();
346 if (mainCommand[0].endsWith(".jar")) {
347
348 args.append("-jar ").append(new File(mainCommand[0]).getPath());
349 } else {
350
351 args.append("-cp \"").append(System.getProperty("java.class.path"))
352 .append("\" ").append(mainCommand[0]);
353 }
354
355 for (int i = 1; i < mainCommand.length; i++) {
356 args.append(' ').append(mainCommand[i]);
357 }
358 return args.toString();
359 }
360 return null;
361 }
362
363
364
365
366
367
368
369 public static void setRestart(final boolean toRestart) {
370 shouldRestart = toRestart;
371 }
372
373
374
375
376
377
378
379 public static void registerMain(final Class<?> main, final String[] args) {
380 if (main == null) {
381 applArgs = getArgs();
382 return;
383 }
384 final String path = ManagementFactory.getRuntimeMXBean().getClassPath();
385 final StringBuilder newArgs = new StringBuilder();
386 if (ParametersChecker.isNotEmpty(path)) {
387 newArgs.append("-cp ").append(path);
388 }
389 newArgs.append(' ').append(main.getName());
390 for (final String arg : args) {
391 newArgs.append(' ').append(arg);
392 }
393 applArgs = newArgs.toString();
394 }
395
396 @Override
397 public void run() {
398 if (isShutdownOver) {
399 if (shutdownHook != null && shutdownHook.serviceStopped()) {
400 try {
401 Thread.sleep(WaarpNettyUtil.SIMPLE_DELAY_MS);
402 } catch (final InterruptedException e) {
403 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
404 }
405 }
406
407 SysErrLogger.FAKE_LOGGER.syserr(
408 "Halt System now - services already stopped -");
409
410 return;
411 }
412 try {
413 terminate(false);
414 } catch (final Throwable t) {
415 if (shutdownHook != null && shutdownHook.serviceStopped()) {
416 try {
417 Thread.sleep(WaarpNettyUtil.SIMPLE_DELAY_MS);
418 } catch (final InterruptedException e) {
419 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
420 }
421 }
422 }
423 SysErrLogger.FAKE_LOGGER.syserr("Halt System now");
424
425 }
426
427
428
429
430 public static class ShutdownConfiguration {
431 public long timeout = 30000;
432 public WaarpFuture serviceFuture;
433 }
434
435
436
437
438 static class ShutdownTimerTask extends TimerTask {
439
440
441
442 private static final WaarpLogger logger =
443 WaarpLoggerFactory.getLogger(ShutdownTimerTask.class);
444
445
446
447
448 ShutdownTimerTask() {
449 }
450
451 @Override
452 public void run() {
453 SysErrLogger.FAKE_LOGGER.syserr("Halt System now - time waiting is over");
454 logger.error("System will force EXIT");
455 if (shutdownHook != null && shutdownHook.serviceStopped()) {
456 try {
457 Thread.sleep(WaarpNettyUtil.SIMPLE_DELAY_MS);
458 } catch (final InterruptedException e) {
459 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
460 }
461 }
462 if (logger.isDebugEnabled()) {
463 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
464 for (final Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
465 printStackTrace(entry.getKey(), entry.getValue());
466 }
467 }
468
469 }
470 }
471 }