Merge "ConfigurationService to create default config dir"
[controller.git] / opendaylight / config / shutdown-impl / src / main / java / org / opendaylight / controller / config / yang / shutdown / impl / ShutdownServiceImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.config.yang.shutdown.impl;
9
10 import com.google.common.base.Optional;
11 import org.opendaylight.controller.config.shutdown.ShutdownService;
12 import org.osgi.framework.Bundle;
13 import org.osgi.framework.BundleException;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import java.lang.management.ManagementFactory;
18 import java.lang.management.ThreadInfo;
19
20 public class ShutdownServiceImpl implements ShutdownService, AutoCloseable {
21     private final ShutdownService impl;
22     private final ShutdownRuntimeRegistration registration;
23
24     public ShutdownServiceImpl(String secret, Bundle systemBundle,
25                                ShutdownRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) {
26         if (secret == null) {
27             throw new IllegalArgumentException("Secret cannot be null");
28         }
29         impl = new Impl(secret, systemBundle);
30         registration = rootRuntimeBeanRegistratorWrapper.register(new MXBeanImpl(impl));
31     }
32
33     @Override
34     public void shutdown(String inputSecret, Long maxWaitTime, Optional<String> reason) {
35         impl.shutdown(inputSecret, maxWaitTime, reason);
36     }
37
38     @Override
39     public void close() {
40         registration.close();
41     }
42 }
43
44 class Impl implements ShutdownService {
45     private static final Logger logger = LoggerFactory.getLogger(Impl.class);
46     private final String secret;
47     private final Bundle systemBundle;
48
49     Impl(String secret, Bundle systemBundle) {
50         this.secret = secret;
51         this.systemBundle = systemBundle;
52     }
53
54     @Override
55     public void shutdown(String inputSecret, Long maxWaitTime, Optional<String> reason) {
56         logger.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
57         try {
58             Thread.sleep(1000); // prevent brute force attack
59         } catch (InterruptedException e) {
60             Thread.currentThread().interrupt();
61             logger.warn("Shutdown process interrupted", e);
62         }
63         if (this.secret.equals(inputSecret)) {
64             logger.info("Server is shutting down");
65
66             // actual work:
67             Thread stopSystemBundleThread = new StopSystemBundleThread(systemBundle);
68             stopSystemBundleThread.start();
69             if (maxWaitTime != null && maxWaitTime > 0) {
70                 Thread systemExitThread = new CallSystemExitThread(maxWaitTime);
71                 logger.debug("Scheduling {}", systemExitThread);
72                 systemExitThread.start();
73             }
74             // end
75         } else {
76             logger.warn("Unauthorized attempt to shut down server");
77             throw new IllegalArgumentException("Invalid secret");
78         }
79     }
80
81 }
82
83 class StopSystemBundleThread extends Thread {
84     private static final Logger logger = LoggerFactory.getLogger(StopSystemBundleThread.class);
85     public static final String CONFIG_MANAGER_SYMBOLIC_NAME = "org.opendaylight.controller.config-manager";
86     private final Bundle systemBundle;
87
88     StopSystemBundleThread(Bundle systemBundle) {
89         super("stop-system-bundle");
90         this.systemBundle = systemBundle;
91     }
92
93     @Override
94     public void run() {
95         try {
96             // wait so that JMX response is received
97             Thread.sleep(1000);
98             // first try to stop config-manager
99             Bundle configManager = findConfigManager();
100             if (configManager != null){
101                 logger.debug("Stopping config-manager");
102                 configManager.stop();
103                 Thread.sleep(1000);
104             }
105             logger.debug("Stopping system bundle");
106             systemBundle.stop();
107         } catch (BundleException e) {
108             logger.warn("Can not stop OSGi server", e);
109         } catch (InterruptedException e) {
110             logger.warn("Shutdown process interrupted", e);
111         }
112     }
113
114     private Bundle findConfigManager() {
115         for(Bundle bundle: systemBundle.getBundleContext().getBundles()){
116             if (CONFIG_MANAGER_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {
117                 return bundle;
118             }
119         }
120         return null;
121     }
122
123 }
124
125 class CallSystemExitThread extends Thread {
126     private static final Logger logger = LoggerFactory.getLogger(CallSystemExitThread.class);
127     private final long maxWaitTime;
128     CallSystemExitThread(long maxWaitTime) {
129         super("call-system-exit-daemon");
130         setDaemon(true);
131         if (maxWaitTime <= 0){
132             throw new IllegalArgumentException("Cannot schedule to zero or negative time:" + maxWaitTime);
133         }
134         this.maxWaitTime = maxWaitTime;
135     }
136
137     @Override
138     public String toString() {
139         return "CallSystemExitThread{" +
140                 "maxWaitTime=" + maxWaitTime +
141                 '}';
142     }
143
144     @Override
145     public void run() {
146         try {
147             // wait specified time
148             Thread.sleep(maxWaitTime);
149             logger.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
150             // do a thread dump
151             ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
152             StringBuffer sb = new StringBuffer();
153             for(ThreadInfo info : threads) {
154                 sb.append(info);
155                 sb.append("\n");
156             }
157             logger.warn("Thread dump:{}", sb);
158             System.exit(1);
159         } catch (InterruptedException e) {
160             logger.warn("Interrupted, not going to call System.exit(1)");
161         }
162     }
163 }
164
165
166 class MXBeanImpl implements ShutdownRuntimeMXBean {
167     private final ShutdownService impl;
168
169     MXBeanImpl(ShutdownService impl) {
170         this.impl = impl;
171     }
172
173     @Override
174     public void shutdown(String inputSecret, Long maxWaitTime, String nullableReason) {
175         Optional<String> optionalReason;
176         if (nullableReason == null) {
177             optionalReason = Optional.absent();
178         } else {
179             optionalReason = Optional.of(nullableReason);
180         }
181         impl.shutdown(inputSecret, maxWaitTime, optionalReason);
182     }
183 }