package de.skuzzle.polly.core.internal;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import de.skuzzle.polly.core.Polly;
import de.skuzzle.polly.process.JavaProcessExecutor;
import de.skuzzle.polly.process.ProcessExecutor;
import de.skuzzle.polly.sdk.CompositeDisposable;
import de.skuzzle.polly.sdk.Disposable;
import de.skuzzle.polly.sdk.ShutdownManager;
import de.skuzzle.polly.sdk.exceptions.DisposingException;
/**
*
* @author Simon
* @version 27.07.2011 ae73250
* @since Beta 0.5
*/
public class ShutdownManagerImpl implements ShutdownManager {
private static Logger logger = Logger.getLogger(ShutdownManagerImpl.class.getName());
/**
* Sun property pointing the main class and its arguments.
* Might not be defined on non Hotspot VM implementations.
*/
public static final String SUN_JAVA_COMMAND = "sun.java.command"; //$NON-NLS-1$
private AtomicBoolean isSafeShutdown;
private CompositeDisposable shutdownList;
public ShutdownManagerImpl() {
this.isSafeShutdown = new AtomicBoolean(false);
this.shutdownList = new CompositeDisposable();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (!ShutdownManagerImpl.this.isSafeShutdown.get()) {
logger.warn("Unexpected shutdown. Trying to shutdown all " + //$NON-NLS-1$
"resources properly."); //$NON-NLS-1$
ShutdownManagerImpl.this.emergencyShutdown();
}
}
});
}
public CompositeDisposable getShutdownList() {
return this.shutdownList;
}
public synchronized void addDisposable(Disposable d) {
this.shutdownList.addLast(d);
}
@Override
public void restart() {
this.restart(Polly.getCommandLine());
}
/*
* Implementation is adapted from
* http://java.dzone.com/articles/programmatically-restart-java
*/
@Override
public synchronized void restart(String commandLine) {
// properly shutdown all loaded components
this.shutdown(false);
logger.info("Preparing to restart..."); //$NON-NLS-1$
logger.debug("Commandline arguments: " + commandLine); //$NON-NLS-1$
try {
final ProcessExecutor pe = JavaProcessExecutor.getCurrentInstance(false);
pe.addCommandsFromString("-jar polly.jar -returninfo \"Returned from restart\""); //$NON-NLS-1$
if (commandLine != null && !commandLine.equals("")) { //$NON-NLS-1$
pe.addCommandsFromString(commandLine);
}
logger.trace("Java command: '" + pe.toString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
// execute the command in a shutdown hook, to be sure that all the
// resources have been disposed before restarting the application
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
pe.start();
logger.trace("Executed: " + pe.toString()); //$NON-NLS-1$
logger.info("New polly instance initialized."); //$NON-NLS-1$
} catch (IOException e) {
logger.error("Could not start new polly instance.", e); //$NON-NLS-1$
}
}
});
} catch (Exception e) {
logger.error("Could not start new polly instance", e); //$NON-NLS-1$
} finally {
// All resources have been unloaded, so perform shutdown
logger.warn("Exiting current polly process."); //$NON-NLS-1$
System.exit(0);
}
}
public synchronized void shutdown(boolean exit) {
this.isSafeShutdown.set(true);
logger.info("Shutting down all components."); //$NON-NLS-1$
try {
this.shutdownList.dispose();
} catch (DisposingException e) {
logger.error("One or more components failed to properly unload. " + //$NON-NLS-1$
"Last exception trace:", e); //$NON-NLS-1$
}
logger.trace("Remaining active threads: " + Thread.activeCount()); //$NON-NLS-1$
logger.trace("Remaining stacktraces:"); //$NON-NLS-1$
this.printRemainingThreads();
if (exit) {
logger.info("All connections closed. Now exiting the whole program. ByeBye"); //$NON-NLS-1$
LogManager.shutdown();
System.exit(0);
}
}
public void shutdown() {
this.shutdown(true);
}
private void emergencyShutdown() {
try {
LogManager.shutdown();
this.shutdownList.dispose();
} catch (DisposingException e) {
e.printStackTrace();
}
}
private void printRemainingThreads() {
Thread[] active = new Thread[Thread.activeCount()];
Thread.enumerate(active);
for (Thread t : active) {
logger.trace("Thread: " + t.toString()); //$NON-NLS-1$
for (StackTraceElement e : t.getStackTrace()) {
logger.trace(" " + e.toString()); //$NON-NLS-1$
}
}
}
}