blob: 2e75f92d517f7d7b81ef84eb9cba039afdadb361 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.nano.core.internal;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.launch.Framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.virgo.nano.core.Shutdown;
import org.eclipse.virgo.kernel.diagnostics.KernelLogEvents;
import org.eclipse.virgo.medic.eventlog.EventLogger;
/**
* Standard implementation of {@link Shutdown} that performs all shutdown actions synchronously.
* <p />
* This implementation registers a {@link Runtime#addShutdownHook(Thread) shutdown hook} to perform graceful shutdown
* when the user kills the VM.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* Threadsafe
*
*/
class ShutdownManager implements Shutdown {
private static final Logger LOGGER = LoggerFactory.getLogger(ShutdownManager.class);
private static final long SHUTDOWN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final int STATE_RUNNING = 0;
private static final int STATE_STOPPING = 1;
private final AtomicInteger state = new AtomicInteger(STATE_RUNNING);
private final EventLogger eventLogger;
private final Framework framework;
private final Thread shutdownHook = new Thread(new Runnable() {
public void run() {
try {
// set to stopping so we don't try to remove the shutdown hook later
state.set(STATE_STOPPING);
ShutdownManager.this.shutdown();
} catch (Throwable t) {
t.printStackTrace();
}
}
});
public ShutdownManager(EventLogger eventLogger, Framework framework) {
this.eventLogger = eventLogger;
this.framework = framework;
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
BundleContext bundleContext = framework.getBundleContext();
bundleContext.addBundleListener(new ShutdownLoggingListener());
}
/**
* {@inheritDoc}
*/
public void shutdown() {
FrameworkEvent shutdownResponse = null;
try {
this.framework.stop();
shutdownResponse = this.framework.waitForStop(SHUTDOWN_TIMEOUT);
} catch (BundleException ex) {
LOGGER.error("Error during shutdown.", ex);
} catch (InterruptedException ex) {
LOGGER.error("Interrupted during shutdown.", ex);
}
if (!isSuccessfulStopResponse(shutdownResponse)) {
immediateShutdown();
} else {
removeShutdownHook();
}
}
/**
* {@inheritDoc}
*/
public void immediateShutdown() {
removeShutdownHook();
this.eventLogger.log(KernelLogEvents.IMMEDIATE_SHUTDOWN_INITIATED);
exitVM();
}
protected void exitVM() {
System.exit(0);
}
private boolean isSuccessfulStopResponse(FrameworkEvent shutdownResponse) {
return shutdownResponse != null && shutdownResponse.getType() == FrameworkEvent.STOPPED;
}
private void removeShutdownHook() {
if(this.state.compareAndSet(STATE_RUNNING, STATE_STOPPING)) {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
}
private final class ShutdownLoggingListener implements SynchronousBundleListener {
public void bundleChanged(BundleEvent event) {
BundleContext bundleContext = ShutdownManager.this.framework.getBundleContext();
if (BundleEvent.STOPPING == event.getType() && event.getBundle() == bundleContext.getBundle()) {
ShutdownManager.this.eventLogger.log(KernelLogEvents.SHUTDOWN_INITIATED);
removeShutdownHook();
}
}
}
}