package org.appwork.shutdown; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.logging.Level; import org.appwork.utils.logging.Log; public class ShutdownController extends Thread { private static final ShutdownController INSTANCE = new ShutdownController(); /** * get the only existing instance of ShutdownController. This is a singleton * * @return */ public static ShutdownController getInstance() { return ShutdownController.INSTANCE; } public static void main(final String[] args) { Log.L.setLevel(Level.ALL); ShutdownController.getInstance().addShutdownEvent(new ShutdownEvent() { @Override public int getHookPriority() { return 1; } @Override public void run() { System.out.println("DO " + this.getHookPriority()); } }); ShutdownController.getInstance().addShutdownEvent(new ShutdownEvent() { @Override public int getHookPriority() { return 3; } @Override public void run() { System.out.println("DO " + this.getHookPriority()); } }); ShutdownController.getInstance().addShutdownEvent(new ShutdownEvent() { @Override public int getHookPriority() { return 2; } @Override public void run() { System.out.println("DO " + this.getHookPriority()); } }); } private final LinkedList<ShutdownEvent> hooks; private final ArrayList<ShutdownVetoListener> vetoListeners; /** * Create a new instance of ShutdownController. This is a singleton class. * Access the only existing instance by using {@link #getInstance()}. */ private ShutdownController() { super(ShutdownController.class.getSimpleName()); this.hooks = new LinkedList<ShutdownEvent>(); this.vetoListeners = new ArrayList<ShutdownVetoListener>(); Runtime.getRuntime().addShutdownHook(this); } /** * @param restartViewUpdaterEvent */ public void addShutdownEvent(final ShutdownEvent event) { if (this.isAlive()) { throw new IllegalStateException("Cannot add hooks during shutdown"); } synchronized (this.hooks) { ShutdownEvent next; int i = 0; // add event sorted for (final Iterator<ShutdownEvent> it = this.hooks.iterator(); it.hasNext();) { next = it.next(); if (next.getHookPriority() <= event.getHookPriority()) { this.hooks.add(i, event); return; } i++; } this.hooks.add(event); } } public void addShutdownVetoListener(final ShutdownVetoListener listener) { synchronized (this.vetoListeners) { this.vetoListeners.remove(listener); this.vetoListeners.add(listener); } } /** * @return */ public ArrayList<ShutdownVetoException> collectVetos() { final ArrayList<ShutdownVetoException> vetos = new ArrayList<ShutdownVetoException>(); synchronized (this.vetoListeners) { for (final ShutdownVetoListener v : this.vetoListeners) { try { v.onShutdownRequest(); } catch (final ShutdownVetoException e) { vetos.add(e); } catch (final Throwable e) { e.printStackTrace(); } } } return vetos; } /** * Same function as org.appwork.utils.Exceptions.getStackTrace(Throwable)<br> * <b>DO NOT REPLACE IT EITHER!</b> Exceptions.class my be unloaded. This * would cause Initialize Exceptions during shutdown. * * @param thread * @return */ private String getStackTrace(final Thread thread) { try { final StackTraceElement[] st = thread.getStackTrace(); final StringBuilder sb = new StringBuilder(""); for (final StackTraceElement element : st) { sb.append(element); sb.append("\r\n"); } return sb.toString(); } catch (final Throwable e) { e.printStackTrace(); return null; } } /** * @param instance2 * @return */ public boolean hasShutdownEvent(final ShutdownEvent instance2) { synchronized (this.hooks) { return this.hooks.contains(instance2); } } public void removeShutdownEvent(final ShutdownEvent event) { if (this.isAlive()) { throw new IllegalStateException("Cannot add hooks during shutdown"); } synchronized (this.hooks) { ShutdownEvent next; // add event sorted for (final Iterator<ShutdownEvent> it = this.hooks.iterator(); it.hasNext();) { next = it.next(); if (next == event) { it.remove(); } } } } public void removeShutdownVetoListener(final ShutdownVetoListener listener) { synchronized (this.vetoListeners) { this.vetoListeners.remove(listener); } } /** * */ public void requestShutdown() { final ArrayList<ShutdownVetoException> vetos = this.collectVetos(); if (vetos.size() == 0) { synchronized (this.vetoListeners) { for (final ShutdownVetoListener v : this.vetoListeners) { try { v.onShutdown(); } catch (final Throwable e) { e.printStackTrace(); } } } System.exit(0); } else { synchronized (this.vetoListeners) { for (final ShutdownVetoListener v : this.vetoListeners) { v.onShutdownVeto(vetos); } } } } @Override public void run() { /* * Attention. This runs in shutdownhook. make sure, that we do not have * to load previous unloaded classes here. For example avoid Log.class * here */ try { synchronized (this.hooks) { for (final ShutdownEvent e : this.hooks) { try { System.out.println("ShutdownController: start item->" + e); e.start(); try { e.join(e.getMaxDuration()); } catch (final Throwable e1) { e1.printStackTrace(); } if (e.isAlive()) { System.out.println("ShutdownController: " + e + "->is still running after " + e.getMaxDuration() + " ms"); System.out.println("ShutdownController: " + e + "->StackTrace:\r\n" + this.getStackTrace(e)); } } catch (final Throwable e1) { e1.printStackTrace(); } } } } catch (final Throwable e1) { // do not use Log here. If Log.exception(e1); throws an exception, // we have to catch it here without the risk of another exception. } } }