package org.opendaylight.controller.config.yang.shutdown.impl;
import com.google.common.base.Optional;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
import org.opendaylight.controller.config.shutdown.ShutdownService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
private final ShutdownService impl;
private final ShutdownRuntimeRegistration registration;
- public ShutdownServiceImpl(String secret, Bundle systemBundle,
- ShutdownRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) {
+ public ShutdownServiceImpl(final String secret, final Bundle systemBundle,
+ final ShutdownRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) {
if (secret == null) {
throw new IllegalArgumentException("Secret cannot be null");
}
}
@Override
- public void shutdown(String inputSecret, Optional<String> reason) {
- impl.shutdown(inputSecret, reason);
+ public void shutdown(final String inputSecret, final Long maxWaitTime, final Optional<String> reason) {
+ impl.shutdown(inputSecret, maxWaitTime, reason);
}
@Override
}
class Impl implements ShutdownService {
- private static final Logger logger = LoggerFactory.getLogger(Impl.class);
+ private static final Logger LOG = LoggerFactory.getLogger(Impl.class);
private final String secret;
private final Bundle systemBundle;
- Impl(String secret, Bundle systemBundle) {
+ Impl(final String secret, final Bundle systemBundle) {
this.secret = secret;
this.systemBundle = systemBundle;
}
@Override
- public void shutdown(String inputSecret, Optional<String> reason) {
- logger.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
+ public void shutdown(final String inputSecret, final Long maxWaitTime, final Optional<String> reason) {
+ LOG.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
try {
Thread.sleep(1000); // prevent brute force attack
- } catch (InterruptedException e) {
+ } catch (final InterruptedException e) {
Thread.currentThread().interrupt();
- logger.warn("Shutdown process interrupted", e);
+ LOG.warn("Shutdown process interrupted", e);
}
if (this.secret.equals(inputSecret)) {
- logger.info("Server is shutting down");
-
- Thread stopSystemBundle = new Thread() {
- @Override
- public void run() {
- try {
- // wait so that JMX response is received
- Thread.sleep(1000);
- systemBundle.stop();
- } catch (BundleException e) {
- logger.warn("Can not stop OSGi server", e);
- } catch (InterruptedException e) {
- logger.warn("Shutdown process interrupted", e);
- }
- }
- };
- stopSystemBundle.start();
+ LOG.info("Server is shutting down");
+ // actual work:
+ Thread stopSystemBundleThread = new StopSystemBundleThread(systemBundle);
+ stopSystemBundleThread.start();
+ if (maxWaitTime != null && maxWaitTime > 0) {
+ Thread systemExitThread = new CallSystemExitThread(maxWaitTime);
+ LOG.debug("Scheduling {}", systemExitThread);
+ systemExitThread.start();
+ }
+ // end
} else {
- logger.warn("Unauthorized attempt to shut down server");
+ LOG.warn("Unauthorized attempt to shut down server");
throw new IllegalArgumentException("Invalid secret");
}
}
}
+class StopSystemBundleThread extends Thread {
+ private static final Logger LOG = LoggerFactory.getLogger(StopSystemBundleThread.class);
+ private final Bundle systemBundle;
+
+ StopSystemBundleThread(final Bundle systemBundle) {
+ super("stop-system-bundle");
+ this.systemBundle = systemBundle;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // wait so that JMX response is received
+ Thread.sleep(1000);
+ LOG.debug("Stopping system bundle");
+ systemBundle.stop();
+ } catch (final BundleException e) {
+ LOG.warn("Can not stop OSGi server", e);
+ } catch (final InterruptedException e) {
+ LOG.warn("Shutdown process interrupted", e);
+ }
+ }
+}
+
+class CallSystemExitThread extends Thread {
+ private static final Logger LOG = LoggerFactory.getLogger(CallSystemExitThread.class);
+ private final long maxWaitTime;
+ CallSystemExitThread(final long maxWaitTime) {
+ super("call-system-exit-daemon");
+ setDaemon(true);
+ if (maxWaitTime <= 0){
+ throw new IllegalArgumentException("Cannot schedule to zero or negative time:" + maxWaitTime);
+ }
+ this.maxWaitTime = maxWaitTime;
+ }
+
+ @Override
+ public String toString() {
+ return "CallSystemExitThread{" +
+ "maxWaitTime=" + maxWaitTime +
+ '}';
+ }
+
+ @Override
+ public void run() {
+ try {
+ // wait specified time
+ Thread.sleep(maxWaitTime);
+ LOG.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
+ // do a thread dump
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
+ StringBuffer sb = new StringBuffer();
+ for(ThreadInfo info : threads) {
+ sb.append(info);
+ sb.append("\n");
+ }
+ LOG.warn("Thread dump:{}", sb);
+ System.exit(1);
+ } catch (final InterruptedException e) {
+ LOG.warn("Interrupted, not going to call System.exit(1)");
+ }
+ }
+}
+
+
class MXBeanImpl implements ShutdownRuntimeMXBean {
private final ShutdownService impl;
- MXBeanImpl(ShutdownService impl) {
+ MXBeanImpl(final ShutdownService impl) {
this.impl = impl;
}
@Override
- public void shutdown(String inputSecret, String nullableReason) {
+ public void shutdown(final String inputSecret, final Long maxWaitTime, final String nullableReason) {
Optional<String> optionalReason;
if (nullableReason == null) {
optionalReason = Optional.absent();
} else {
optionalReason = Optional.of(nullableReason);
}
- impl.shutdown(inputSecret, optionalReason);
+ impl.shutdown(inputSecret, maxWaitTime, optionalReason);
}
}