/* * RHQ Management Platform * Copyright (C) 2005-2014 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.server; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.ConnectionFactory; import org.mc4j.ems.connection.EmsConnection; import org.mc4j.ems.connection.settings.ConnectionSettings; import org.mc4j.ems.connection.support.ConnectionProvider; import org.mc4j.ems.connection.support.metadata.JSR160ConnectionTypeDescriptor; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.plugins.jmx.JMXComponent; /** * This resource serves as the parent to MBeans being exposed for management via plugins injecting child resources. * This resource provides the connection to the parent AS7's MBeanServer. * * Note that this resource must be configured to have its own hostname - this is because this could be different * than the public binding address. The hostname this resource needs is called "jboss.bind.address.management" in the AS7 * configuration. * * Additionally, the username and password are that of a valid management user defined for the AS7 instance. * * @author Jay Shaughnessy * @author John Mazzitelli */ public class JBossAS7JMXComponent<T extends ResourceComponent<?>> implements ResourceComponent<T>, JMXComponent<T> { private static final Log LOG = LogFactory.getLog(JBossAS7JMXComponent.class); public static final String PLUGIN_CONFIG_CLIENT_JAR_LOCATION = "clientJarLocation"; public static final String PLUGIN_CONFIG_PORT = "port"; public static final String PLUGIN_CONFIG_HOSTNAME = "hostname"; // jboss.bind.address.management public static final String PLUGIN_CONFIG_USERNAME = "username"; public static final String PLUGIN_CONFIG_PASSWORD = "password"; public static final String DEFAULT_PLUGIN_CONFIG_PORT = "9999"; private ResourceContext<T> resourceContext; private EmsConnection connection; /** * Controls the dampening of connection error stack traces in an attempt to control spam to the log * file. Each time a connection error is encountered, this will be incremented. When the connection * is finally established, this will be reset to zero. */ private int consecutiveConnectionErrors; @Override public AvailabilityType getAvailability() { try { EmsConnection conn = getEmsConnection(); if (conn == null) { return AvailabilityType.DOWN; } conn.queryBeans("java.lang:*"); // just make a request over the connection to make sure its valid return AvailabilityType.UP; } catch (Throwable t) { try { this.connection.close(); // try to clean up } catch (Throwable ignore) { } this.connection = null; return AvailabilityType.DOWN; } } @Override public void start(ResourceContext<T> context) throws Exception { this.resourceContext = context; loadConnection(); } @Override public void stop() { if(connection != null) { connection.close(); } connection = null; } @Override public EmsConnection getEmsConnection() { EmsConnection emsConnection = null; try { emsConnection = loadConnection(); } catch (Exception e) { LOG.error("Component attempting to access a connection that could not be loaded: " + e.getCause()); } return emsConnection; } private EmsConnection loadConnection() throws Exception { if (connection != null) { return connection; } try { Configuration pluginConfig = resourceContext.getPluginConfiguration(); String hostname = pluginConfig.getSimpleValue(PLUGIN_CONFIG_HOSTNAME, "127.0.0.1"); String port = pluginConfig.getSimpleValue(PLUGIN_CONFIG_PORT, DEFAULT_PLUGIN_CONFIG_PORT); String username = pluginConfig.getSimpleValue(PLUGIN_CONFIG_USERNAME, "rhqadmin"); String password = pluginConfig.getSimpleValue(PLUGIN_CONFIG_PASSWORD, "rhqadmin"); String clientJarDir = pluginConfig.getSimpleValue(PLUGIN_CONFIG_CLIENT_JAR_LOCATION); ConnectionSettings connectionSettings = new ConnectionSettings(); connectionSettings.setLibraryURI(clientJarDir); connectionSettings.initializeConnectionType(new RhqServerConnectionTypeDescriptor("jboss-client.jar")); connectionSettings.setServerUrl("service:jmx:remoting-jmx://" + hostname + ":" + port); connectionSettings.setPrincipal(username); connectionSettings.setCredentials(password); if (connectionSettings.getControlProperties() == null) { connectionSettings.setControlProperties(new Properties()); } connectionSettings.getControlProperties().setProperty(ConnectionFactory.COPY_JARS_TO_TEMP, String.valueOf(TRUE)); connectionSettings.getControlProperties().setProperty(ConnectionFactory.JAR_TEMP_DIR, resourceContext.getTemporaryDirectory().getAbsolutePath()); if (connectionSettings.getAdvancedProperties() == null) { connectionSettings.setAdvancedProperties(new Properties()); } connectionSettings.getAdvancedProperties().setProperty(ConnectionFactory.USE_CONTEXT_CLASSLOADER, String.valueOf(FALSE)); ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.discoverServerClasses(connectionSettings); LOG.info("Loading AS7 connection [" + connectionSettings.getServerUrl() + "] with install path [" + connectionSettings.getLibraryURI() + "]..."); ConnectionProvider connectionProvider = connectionFactory.getConnectionProvider(connectionSettings); this.connection = connectionProvider.connect(); if (LOG.isDebugEnabled()) { LOG.debug("Successfully made connection to the AS7 instance for resource [" + resourceContext.getResourceKey() + "]"); } return connection; } catch (Exception e) { // The connection will be established even in the case that the principal cannot be authenticated, // but the connection will not work. That failure seems to come from the call to loadSynchronous after // the connection is established. If we get to this point that an exception was thrown, close any // connection that was made and null it out so we can try to establish it again. if (connection != null) { if (LOG.isDebugEnabled()) { LOG.debug("Connection created but an exception was thrown. Closing the connection.", e); } connection.close(); connection = null; } // Since the connection is attempted each time it's used, failure to connect could result in log // file spamming. Log it once for every 10 consecutive times it's encountered. if (consecutiveConnectionErrors % 10 == 0) { LOG.warn("Could not establish connection to the JBoss AS instance [" + (consecutiveConnectionErrors + 1) + "] times for resource [" + resourceContext.getResourceKey() + "]", e); } consecutiveConnectionErrors++; if (LOG.isDebugEnabled()) { LOG.debug("Can't connect to JBoss AS resource [" + resourceContext.getResourceKey() + "]", e); } throw e; } } private static class RhqServerConnectionTypeDescriptor extends JSR160ConnectionTypeDescriptor { private final String clientJarFilename; RhqServerConnectionTypeDescriptor(String clientJarFilename) { this.clientJarFilename = clientJarFilename; } @Override public String[] getConnectionClasspathEntries() { return new String[] { clientJarFilename }; } @Override public boolean isUseChildFirstClassLoader() { return true; } } }