Method: ShutdownManager.ShutdownLoggingListener(ShutdownManager)

1: /*******************************************************************************
2: * Copyright (c) 2008, 2010 VMware Inc.
3: * All rights reserved. This program and the accompanying materials
4: * are made available under the terms of the Eclipse Public License v1.0
5: * which accompanies this distribution, and is available at
6: * http://www.eclipse.org/legal/epl-v10.html
7: *
8: * Contributors:
9: * VMware Inc. - initial contribution
10: *******************************************************************************/
11:
12: package org.eclipse.virgo.nano.core.internal;
13:
14: import java.util.concurrent.TimeUnit;
15: import java.util.concurrent.atomic.AtomicInteger;
16:
17: import org.eclipse.virgo.medic.eventlog.EventLogger;
18: import org.eclipse.virgo.nano.core.Shutdown;
19: import org.eclipse.virgo.nano.diagnostics.KernelLogEvents;
20: import org.osgi.framework.BundleContext;
21: import org.osgi.framework.BundleEvent;
22: import org.osgi.framework.BundleException;
23: import org.osgi.framework.FrameworkEvent;
24: import org.osgi.framework.SynchronousBundleListener;
25: import org.osgi.framework.launch.Framework;
26: import org.slf4j.Logger;
27: import org.slf4j.LoggerFactory;
28:
29: /**
30: * Standard implementation of {@link Shutdown} that may be called to initiate JVM shutdown. This class also listens for
31: * <i>unsolicited</i> shutdown (that is, shutdown not initiated by this class) of the JVM or the OSGi framework and
32: * reacts accordingly.
33: * <p />
34: * JVM shutdown may be initiated in either of two ways:
35: * <ol>
36: * <li>By this class (when a program calls one of the methods of the {@link Shutdown} interface). If graceful shutdown
37: * is requested, this class shuts down the OSGi framework and then the JVM. If immediate shutdown is requested, this
38: * class shuts down the JVM.</li>
39: * <li>Not by this class. A {@link Runtime#addShutdownHook(Thread) shutdown hook} previously registered by this class
40: * responds to JVM shutdown by shutting down the OSGi framework before allowing JVM shutdown to continue. Note that
41: * {@link System#exit} must not be called under the shutdown hook as this can block indefinitely (see the javadoc of
42: * {@link Runtime#exit}).</li>
43: * </ol>
44: * If this class attempts to shut down the OSGi framework but this takes longer than {@code SHUTDOWN_TIMOUT}, an error
45: * message is written to the event log and the JVM is halted abruptly with a non-zero exit code.
46: * <p />
47: * When OSGi framework shutdown occurs, a synchronous bundle listener previously registered by this class unregisters
48: * the shutdown hook (unless this class initiated the OSGi framework shutdown in response to an unsolicited JVM
49: * shutdown, in which case the shutdown hook is left in place).</li>
50: * <p />
51: * This class expects some recursive invocations and avoids others:
52: * <ul>
53: * <li>A graceful shutdown request on the {@link Shutdown} interface normally results in the synchronous bundle listener
54: * being driven on OSGi framework shutdown.</li>
55: * <li>An immediate shutdown request on the {@link Shutdown} interface unregisters the shutdown hook so that it is not
56: * driven when the JVM is shut down.</li>
57: * <li>An unsolicited JVM termination prevents the shutdown hook from being unregistered.</li>
58: * </ul>
59: * <p />
60: * So, in summary, the JVM may terminate in the following ways:
61: * <ol>
62: * <li>Solicited graceful shutdown, which attempts to stop the OSGi framework and, if successful, exits the JVM.</li>
63: * <li>Unsolicited graceful shutdown, which attempts to stop the OSGi framework and, if successful, allows JVM
64: * termination to continue.</li>
65: * <li>Solicited immediate shutdown, which exits the JVM.</li>
66: * <li>Solicited halt if an attempt by this class to stop the OSGi framework fails or times out.</li>
67: * <li>Unsolicited halt or other abrupt termination (such as "kill -9" or a power failure), which does not involve this
68: * class.</li>
69: * </ol>
70: * <p />
71: * <strong>Concurrent Semantics</strong><br />
72: *
73: * Threadsafe
74: *
75: */
76: class ShutdownManager implements Shutdown {
77:
78: private static final int NORMAL_TERMINATION_EXIT_CODE = 0;
79:
80: private static final int GRACEFUL_TERMINATION_FAILURE_EXIT_CODE = 1;
81:
82: private static final Logger LOGGER = LoggerFactory.getLogger(ShutdownManager.class);
83:
84: private static final long SHUTDOWN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
85:
86: private static final int STATE_RUNNING = 0;
87:
88: private static final int STATE_STOPPING = 1;
89:
90: private final AtomicInteger state = new AtomicInteger(STATE_RUNNING);
91:
92: private final EventLogger eventLogger;
93:
94: private final Framework framework;
95:
96: private final Runtime runtime;
97:
98: private final Thread shutdownHook = new Thread(new Runnable() {
99:
100: @Override
101: public void run() {
102: try {
103: // set to stopping so we don't remove the shutdown hook later
104: if (ShutdownManager.this.compareAndSetHookStopping()) {
105: ShutdownManager.this.doShutdown(false);
106: }
107: } catch (Throwable t) {
108: t.printStackTrace();
109: }
110: }
111:
112: });
113:
114: public ShutdownManager(EventLogger eventLogger, Framework framework, Runtime runtime) {
115: this.eventLogger = eventLogger;
116: this.framework = framework;
117: this.runtime = runtime;
118: runtime.addShutdownHook(this.shutdownHook);
119: BundleContext bundleContext = framework.getBundleContext();
120: bundleContext.addBundleListener(new ShutdownLoggingListener());
121: }
122:
123: /**
124: * {@inheritDoc}
125: */
126: @Override
127: public void shutdown() {
128: doShutdown(true);
129: }
130:
131: private void doShutdown(boolean solicitedShutdown) {
132: FrameworkEvent shutdownResponse = null;
133: try {
134: this.framework.stop();
135: shutdownResponse = this.framework.waitForStop(SHUTDOWN_TIMEOUT);
136: } catch (BundleException ex) {
137: LOGGER.error("Error during shutdown.", ex);
138: } catch (InterruptedException ex) {
139: LOGGER.error("Interrupted during shutdown.", ex);
140: }
141:
142: if (isSuccessfulStopResponse(shutdownResponse)) {
143: // If this class initiated shutdown, shut down the JVM. Otherwise allow JVM termination to continue.
144: if (solicitedShutdown) {
145: initiateJvmTermination();
146: }
147: } else {
148: // Escalate to JVM halt.
149: this.eventLogger.log(KernelLogEvents.SHUTDOWN_HALTED);
150: haltJvm(GRACEFUL_TERMINATION_FAILURE_EXIT_CODE);
151: }
152: }
153:
154: private void initiateJvmTermination() {
155: removeShutdownHook();
156: exitJvm();
157: }
158:
159: /**
160: * {@inheritDoc}
161: */
162: @Override
163: public void immediateShutdown() {
164: this.eventLogger.log(KernelLogEvents.IMMEDIATE_SHUTDOWN_INITIATED);
165: initiateJvmTermination();
166: }
167:
168: /**
169: * This method must not be overridden except by testcases.
170: */
171: protected void exitJvm() {
172: System.exit(NORMAL_TERMINATION_EXIT_CODE);
173: }
174:
175: /**
176: * This method must not be overridden except by testcases.
177: */
178: protected void haltJvm(int status) {
179: this.runtime.halt(status);
180: }
181:
182: private boolean isSuccessfulStopResponse(FrameworkEvent shutdownResponse) {
183: return shutdownResponse != null && shutdownResponse.getType() == FrameworkEvent.STOPPED;
184: }
185:
186: /**
187: * This method must only be called by testcases.
188: */
189: final void removeShutdownHook() {
190: if (compareAndSetHookStopping()) {
191: this.runtime.removeShutdownHook(this.shutdownHook);
192: }
193: }
194:
195: private boolean compareAndSetHookStopping() {
196: return this.state.compareAndSet(STATE_RUNNING, STATE_STOPPING);
197: }
198:
199: private final class ShutdownLoggingListener implements SynchronousBundleListener {
200:
201: @Override
202: public void bundleChanged(BundleEvent event) {
203: BundleContext bundleContext = ShutdownManager.this.framework.getBundleContext();
204: if (BundleEvent.STOPPING == event.getType() && event.getBundle() == bundleContext.getBundle()) {
205: ShutdownManager.this.eventLogger.log(KernelLogEvents.SHUTDOWN_INITIATED);
206: removeShutdownHook();
207: }
208: }
209: }
210:
211: }