/* * RHQ Management Platform * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.plugins.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.EmsConnection; import org.mc4j.ems.connection.support.ConnectionProvider; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.plugins.jmx.util.ConnectionProviderFactory; /** * The generic JMX server component used to create and cache a connection to a local or * remote JMX MBeanServer. This component is responsible for building an isolated connection/classloader * to the managed resource's JMX MBeanServer. Each connection is isolated from other connections * created by other instances of this component. This allows for it to do things like manage * multiple JBossAS servers that are running on the same box, even if they are of different JBossAS * versions. The same holds true for Hibernate applications - multiple connections can be created * to different versions of the Hibernate MBean and due to the isolation of each connection, there * are no version incompatibility errors that will occur. * * @author Greg Hinkle * @author John Mazzitelli */ public class JMXServerComponent<T extends ResourceComponent<?>> implements JMXComponent<T> { private static final Log log = LogFactory.getLog(JMXServerComponent.class); private volatile EmsConnection connection; private volatile ConnectionProvider connectionProvider; /** * The context of a component that is started. Note, other classes should use #getResourceContext(), rather than * this field. */ ResourceContext context; public void start(ResourceContext context) throws Exception { this.context = context; log.debug("Starting connection to " + context.getResourceType() + "[" + context.getResourceKey() + "]..."); // If connecting to the EMS fails, log a warning but still succeed in starting. getAvailability() will keep // trying to connect each time it is called. try { internalStart(); } catch (Exception e) { if (e.getCause() instanceof SecurityException) { throw new InvalidPluginConfigurationException("Failed to authenticate to managed JVM - " + "principal and/or credentials connection properties are not set correctly."); } // don't litter agent log with a stack trace unless we're in debug if (log.isDebugEnabled()) { log.warn("Failed to connect to " + context.getResourceType() + "[" + context.getResourceKey() + "].", e); } else { log.warn("Failed to connect to " + context.getResourceType() + "[" + context.getResourceKey() + "]: " + e.getMessage()); } } } protected void internalStart() throws Exception { Configuration pluginConfig = context.getPluginConfiguration(); String connectionTypeDescriptorClassName = pluginConfig.getSimple(JMXDiscoveryComponent.CONNECTION_TYPE) .getStringValue(); if (JMXDiscoveryComponent.PARENT_TYPE.equals(connectionTypeDescriptorClassName)) { // Our parent is itself a JMX component, so just reuse its connection, if it has one. this.connection = ((JMXComponent) context.getParentResourceComponent()).getEmsConnection(); if (null == this.connection) { throw new IllegalStateException("Could not access parent connection, parent may be down"); } this.connectionProvider = this.connection.getConnectionProvider(); } else { this.connectionProvider = ConnectionProviderFactory.createConnectionProvider(pluginConfig, this.context.getNativeProcess(), this.context.getTemporaryDirectory()); this.connection = this.connectionProvider.connect(); if (null == this.connection) { throw new IllegalStateException("Failed to create connection, resource may be down"); } this.connection.loadSynchronous(false); } } public void stop() { if (connection != null) { try { connection.close(); } catch (Exception e) { log.error("Error closing EMS connection: " + e); } connection = null; } } public EmsConnection getEmsConnection() { this.reconnectIfDisconnected(); return this.connection; } public AvailabilityType getAvailability() { this.reconnectIfDisconnected(); return ((connectionProvider != null) && connectionProvider.isConnected()) ? AvailabilityType.UP : AvailabilityType.DOWN; } protected ResourceContext getResourceContext() { return this.context; } /** * This method will attempt to reestablish the connection to the JMX server * if it detects that the current connection is no longer open or valid. * * The code that detects if a connection is still open or valid is in the external * EMS library and depends on the type of connection provider. Please read the EMS * documentation and code for more details on how ConnectionProvider.isConnected() * works for different supported providers. */ private synchronized void reconnectIfDisconnected() { if ((connectionProvider) == null || !connectionProvider.isConnected()) { if (connection != null) { try { connection.close(); } catch (Exception e) { log.debug("Unable to close existing EmsConnection for " + context.getResourceType() + "[" + context.getResourceKey() + "] due to error: " + e); } } try { internalStart(); } catch (Exception e) { log.debug("Unable to reconnect to " + context.getResourceType() + "[" + context.getResourceKey() + "] due to error: " + e); } } } }