/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2008-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.vmmgr;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import org.apache.log4j.LogManager;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.service.types.InvokeAtType;
import org.opennms.netmgt.icmp.Pinger;
import org.opennms.netmgt.icmp.PingerFactory;
/**
* <p>
* The Manager is reponsible for launching/starting all services in the VM
* that it is started for. The Manager operates in two modes, normal and
* server
* </p>
* <p>
* normal mode: In the normal mode, the Manager starts all services configured
* for its VM in the service-configuration.xml and starts listening for
* control events on the 'control-broadcast' JMS topic for stop control
* messages for itself
* </p>
* <p>
* server mode: In the server mode, the Manager starts up and listens on the
* 'control-broadcast' JMS topic for 'start' control messages for services in
* its VM and a stop control messge for itself. When a start for a service is
* received, it launches only that service and sends a successful 'running' or
* an 'error' response to the Controller
* </p>
* <p>
* <strong>Note: </strong>The Manager is NOT intelligent - if it receives a
* stop control event, it will exit - does not check to see if the services
* its started are all stopped
* <p>
*
* @author <a href="mailto:weave@oculan.com">Brian Weaver</a>
* @author <a href="mailto:sowmya@opennms.org">Sowmya Nataraj</a>
* @author <a href="http://www.opennms.org">OpenNMS.org</a>
*/
public class Manager implements ManagerMBean {
/**
* The log4j category used to log debug messages and statements.
*/
private static final String LOG4J_CATEGORY = "OpenNMS.Manager";
/**
* <p>stop</p>
*/
public void stop() {
setLogPrefix();
for (MBeanServer server : getMBeanServers()) {
stop(server);
}
}
private void stop(MBeanServer server) {
log().debug("Beginning shutdown");
Invoker invoker = new Invoker();
invoker.setServer(server);
invoker.setAtType(InvokeAtType.STOP);
invoker.setReverse(true);
invoker.setFailFast(false);
List<InvokerService> services = InvokerService.createServiceList(Invoker.getDefaultServiceConfigFactory().getServices());
invoker.setServices(services);
invoker.getObjectInstances();
invoker.invokeMethods();
log().debug("Shutdown complete");
}
/**
* <p>status</p>
*
* @return a {@link java.util.List} object.
*/
public List<String> status() {
setLogPrefix();
List<String> result = new ArrayList<String>();
for (MBeanServer server : getMBeanServers()) {
result.addAll(status(server));
}
return result;
}
private List<String> status(final MBeanServer server) {
log().debug("Beginning status check");
final Invoker invoker = new Invoker();
invoker.setServer(server);
invoker.setAtType(InvokeAtType.STATUS);
invoker.setFailFast(false);
final List<InvokerService> services = InvokerService.createServiceList(Invoker.getDefaultServiceConfigFactory().getServices());
invoker.setServices(services);
invoker.getObjectInstances();
final List<InvokerResult> results = invoker.invokeMethods();
final List<String> statusInfo = new ArrayList<String>(results.size());
for (final InvokerResult invokerResult : results) {
if (invokerResult.getThrowable() == null) {
statusInfo.add("Status: " + invokerResult.getMbean().getObjectName() + " = " + invokerResult.getResult().toString());
} else {
statusInfo.add("Status: " + invokerResult.getMbean().getObjectName() + " = STATUS_CHECK_ERROR");
}
}
log().debug("Status check complete");
return statusInfo;
}
/**
* Uncleanly shutdown OpenNMS. This method calls
* {@see java.lang.System.exit(int)}, which causes the JVM to
* exit immediately. This method is usually invoked via JMX from
* another process as the last stage of shutting down OpenNMS.
*/
public void doSystemExit() {
setLogPrefix();
log().debug("doSystemExit called");
if (log().isDebugEnabled()) {
dumpThreads();
Runtime r = Runtime.getRuntime();
log().debug("memory usage (free/used/total/max allowed): " + r.freeMemory() + "/" + (r.totalMemory() - r.freeMemory()) + "/" + r.totalMemory() + "/" + (r.maxMemory() == Long.MAX_VALUE ? "infinite" : r.maxMemory()));
}
log().info("calling System.exit(1)");
shutdownLogging();
System.exit(1);
}
private void dumpThreads() {
Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
int daemons = 0;
for (Thread t : threads.keySet()) {
if (t.isDaemon()) {
daemons++;
}
}
log().debug("Thread dump of " + threads.size() + " threads (" + daemons + " daemons):");
Map<Thread, StackTraceElement[]> sortedThreads = new TreeMap<Thread, StackTraceElement[]>(new Comparator<Thread>() {
public int compare(Thread t1, Thread t2) {
return new Long(t1.getId()).compareTo(new Long(t2.getId()));
}
});
sortedThreads.putAll(threads);
for (Entry<Thread, StackTraceElement[]> entry : sortedThreads.entrySet()) {
Thread thread = entry.getKey();
log().debug("Thread " + thread.getId() + (thread.isDaemon() ? " (daemon)" : "") + ": " + thread + " (state: " + thread.getState() + ")");
for (StackTraceElement e : entry.getValue()) {
log().debug("\t" + e);
}
}
log().debug("Thread dump completed.");
}
private void shutdownLogging() {
LogManager.shutdown();
}
/**
* <p>doTestLoadLibraries</p>
*/
public void doTestLoadLibraries() {
setLogPrefix();
testPinger();
testGetLocalHost();
}
private void testGetLocalHost() {
try {
InetAddress.getLocalHost();
} catch (UnknownHostException e) {
throw new UndeclaredThrowableException(e, "Could not lookup the host name for the local host machine: " + e);
}
}
private void testPinger() {
final Pinger pinger = PingerFactory.getInstance();
final boolean hasV4 = pinger.isV4Available();
final boolean hasV6 = pinger.isV6Available();
log().info("Using ICMP implementation: " + pinger.getClass().getName());
log().info("IPv4 ICMP available? " + hasV4);
log().info("IPv6 ICMP available? " + hasV6);
if (!hasV4) {
try {
pinger.initialize4();
} catch (final Exception e) {
log().warn("Failed to initialize IPv4 stack.", e);
}
}
if (!hasV6) {
try {
pinger.initialize6();
} catch (final Exception e) {
log().warn("Failed to initialize IPv6 stack.", e);
}
}
final String requireV4String = System.getProperty("org.opennms.netmgt.icmp.requireV4", "detect");
final String requireV6String = System.getProperty("org.opennms.netmgt.icmp.requireV6", "detect");
String errorMessage = null;
if ("true".equalsIgnoreCase(requireV4String) && !hasV4) {
errorMessage = "org.opennms.netmgt.icmp.requireV4 is true, but IPv4 ICMP could not be initialized.";
}
if ("true".equalsIgnoreCase(requireV6String) && !hasV6) {
errorMessage = "org.opennms.netmgt.icmp.requireV6 is true, but IPv6 ICMP could not be initialized.";
}
// If they don't specify any preference, start up as long as one available.
if ("detect".equals(requireV4String) || "detect".equals(requireV6String)) {
if (!hasV4 && !hasV6) {
errorMessage = "Unable to initialize any ICMP support. Bailing out.";
}
}
if (errorMessage != null) {
final String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
errorMessage += " On Windows, you can see this error if you are not running OpenNMS in an Administrator shell.";
}
throw new IllegalStateException(errorMessage);
}
}
private void setLogPrefix() {
ThreadCategory.setPrefix(LOG4J_CATEGORY);
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
private List<MBeanServer> getMBeanServers() {
return MBeanServerFactory.findMBeanServer(null);
}
}