/******************************************************************************* * 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.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.management.Attribute; import javax.management.MBeanServer; import javax.management.ObjectInstance; import javax.management.ObjectName; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.config.ServiceConfigFactory; import org.opennms.netmgt.config.service.Argument; import org.opennms.netmgt.config.service.Invoke; import org.opennms.netmgt.config.service.Service; import org.opennms.netmgt.config.service.types.InvokeAtType; /** * <p> * The Manager is responsible 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 message 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> */ public class Invoker { private MBeanServer m_server; private InvokeAtType m_atType; private boolean m_reverse = false; private boolean m_failFast = true; private List<InvokerService> m_services; /** * <p>Constructor for Invoker.</p> */ public Invoker() { } /** * <p>getDefaultServiceConfigFactory</p> * * @return a {@link org.opennms.netmgt.config.ServiceConfigFactory} object. */ public static ServiceConfigFactory getDefaultServiceConfigFactory() { try { ServiceConfigFactory.init(); return ServiceConfigFactory.getInstance(); } catch (Throwable t) { throw new UndeclaredThrowableException(t); } } /** * <p>instantiateClasses</p> */ public void instantiateClasses() { /* * Preload the classes and register a new instance with the * MBeanServer. */ for (InvokerService invokerService : getServices()) { Service service = invokerService.getService(); try { // preload the class if (log().isDebugEnabled()) { log().debug("loading class " + service.getClassName()); } Class<?> clazz = Class.forName(service.getClassName()); // Get a new instance of the class if (log().isDebugEnabled()) { log().debug("create new instance of " + service.getClassName()); } String log4jPrefix = ThreadCategory.getPrefix(); Object bean; try { bean = clazz.newInstance(); } finally { ThreadCategory.setPrefix(log4jPrefix); } // Register the mbean if (log().isDebugEnabled()) { log().debug("registering mbean instance " + service.getName()); } ObjectName name = new ObjectName(service.getName()); invokerService.setMbean(getServer().registerMBean(bean, name)); // Set attributes org.opennms.netmgt.config.service.Attribute[] attribs = service.getAttribute(); if (attribs != null) { for (org.opennms.netmgt.config.service.Attribute attrib : attribs) { if (log().isDebugEnabled()) { log().debug("setting attribute " + attrib.getName()); } getServer().setAttribute(name, getAttribute(attrib)); } } } catch (Throwable t) { log().error("An error occurred loading the mbean " + service.getName() + " of type " + service.getClassName() + ": " + t, t); invokerService.setBadThrowable(t); } } } /** * <p>getObjectInstances</p> */ public void getObjectInstances() { for (InvokerService invokerService : getServices()) { Service service = invokerService.getService(); try { // find the mbean if (log().isDebugEnabled()) { log().debug("finding mbean instance " + service.getName()); } ObjectName name = new ObjectName(service.getName()); invokerService.setMbean(getServer().getObjectInstance(name)); } catch (Throwable t) { log().error("An error occurred loading the mbean " + service.getName() + " of type " + service.getClassName() + " it will be skipped", t); invokerService.setBadThrowable(t); } } } /** * <p>invokeMethods</p> * * @return a {@link java.util.List} object. */ public List<InvokerResult> invokeMethods() { List<InvokerService> invokerServicesOrdered; if (isReverse()) { invokerServicesOrdered = new ArrayList<InvokerService>(getServices()); Collections.reverse(invokerServicesOrdered); } else { // We can use the original list invokerServicesOrdered = getServices(); } List<InvokerResult> resultInfo = new ArrayList<InvokerResult>(invokerServicesOrdered.size()); for (int pass = 0, end = getLastPass(); pass <= end; pass++) { if (log().isDebugEnabled()) { log().debug("starting pass " + pass); } for (InvokerService invokerService : invokerServicesOrdered) { Service service = invokerService.getService(); String name = invokerService.getService().getName(); ObjectInstance mbean = invokerService.getMbean(); if (invokerService.isBadService()) { resultInfo.add(new InvokerResult(service, mbean, null, invokerService.getBadThrowable())); if (isFailFast()) { return resultInfo; } } for (Invoke invoke : invokerService.getService().getInvoke()) { if (invoke.getPass() != pass || !getAtType().equals(invoke.getAt())) { continue; } if (log().isDebugEnabled()) { log().debug("pass " + pass + " on service " + name + " will invoke method \"" + invoke.getMethod() + "\""); } try { Object result = invoke(invoke, mbean); resultInfo.add(new InvokerResult(service, mbean, result, null)); } catch (Throwable t) { resultInfo.add(new InvokerResult(service, mbean, null, t)); if (isFailFast()) { return resultInfo; } } } } if (log().isDebugEnabled()) { log().debug("completed pass " + pass); } } return resultInfo; } /** * Get the last pass for a set of InvokerServices. * * @param invokerServices list to look at * @return highest pass value found for all Invoke objects in the * invokerServices list */ private int getLastPass() { List<InvokerService> invokerServices = getServices(); int end = 0; for (InvokerService invokerService : invokerServices) { Invoke[] invokes = invokerService.getService().getInvoke(); if (invokes == null) { continue; } for (Invoke invoke : invokes) { if (invoke.getPass() > end) { end = invoke.getPass(); } } } return end; } private Object invoke(Invoke invoke, ObjectInstance mbean) throws Throwable { Argument[] args = invoke.getArgument(); Object[] parms = new Object[0]; String[] sig = new String[0]; if (args != null && args.length > 0) { parms = new Object[args.length]; sig = new String[args.length]; for (int k = 0; k < parms.length; k++) { try { parms[k] = getArgument(args[k]); } catch (Throwable t) { log().error("An error occurred building argument " + k + " for operation "+ invoke.getMethod() + " on MBean " + mbean.getObjectName() + ": " + t, t); throw t; } sig[k] = parms[k].getClass().getName(); } } if (log().isDebugEnabled()) { log().debug("Invoking " + invoke.getMethod() + " on object " + mbean.getObjectName()); } Object object; try { String log4jPrefix = ThreadCategory.getPrefix(); try { object = getServer().invoke(mbean.getObjectName(), invoke.getMethod(), parms, sig); } finally { ThreadCategory.setPrefix(log4jPrefix); } } catch (Throwable t) { log().error("An error occurred invoking operation " + invoke.getMethod() + " on MBean " + mbean.getObjectName() + ": " + t, t); throw t; } log().debug("Invocation successful."); return object; } private Attribute getAttribute(org.opennms.netmgt.config.service.Attribute attrib) throws Exception { Class<?> attribClass = Class.forName(attrib.getValue().getType()); Constructor<?> construct = attribClass.getConstructor(new Class[] { String.class }); Object value; String log4jPrefix = ThreadCategory.getPrefix(); try { value = construct.newInstance(new Object[] { attrib.getValue().getContent() }); } finally { ThreadCategory.setPrefix(log4jPrefix); } return new Attribute(attrib.getName(), value); } private Object getArgument(Argument arg) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Class<?> argClass = Class.forName(arg.getType()); Constructor<?> construct = argClass.getConstructor(new Class[] { String.class }); String log4jPrefix = ThreadCategory.getPrefix(); try { return construct.newInstance(new Object[] { arg.getContent() }); } finally { ThreadCategory.setPrefix(log4jPrefix); } } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } /** * <p>getAtType</p> * * @return a {@link org.opennms.netmgt.config.service.types.InvokeAtType} object. */ public InvokeAtType getAtType() { return m_atType; } /** * <p>setAtType</p> * * @param atType a {@link org.opennms.netmgt.config.service.types.InvokeAtType} object. */ public void setAtType(InvokeAtType atType) { m_atType = atType; } /** * <p>isFailFast</p> * * @return a boolean. */ public boolean isFailFast() { return m_failFast; } /** * <p>setFailFast</p> * * @param failFast a boolean. */ public void setFailFast(boolean failFast) { m_failFast = failFast; } /** * <p>isReverse</p> * * @return a boolean. */ public boolean isReverse() { return m_reverse; } /** * <p>setReverse</p> * * @param reverse a boolean. */ public void setReverse(boolean reverse) { m_reverse = reverse; } /** * <p>getServer</p> * * @return a {@link javax.management.MBeanServer} object. */ public MBeanServer getServer() { return m_server; } /** * <p>setServer</p> * * @param server a {@link javax.management.MBeanServer} object. */ public void setServer(MBeanServer server) { m_server = server; } /** * <p>getServices</p> * * @return a {@link java.util.List} object. */ public List<InvokerService> getServices() { return m_services; } /** * <p>setServices</p> * * @param services a {@link java.util.List} object. */ public void setServices(List<InvokerService> services) { m_services = services; } }