org.rhq.plugins.jslee.JainSleeServerDiscoveryComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.plugins.jslee.JainSleeServerDiscoveryComponent.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.rhq.plugins.jslee;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.slee.management.SleeManagementMBean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.event.log.LogFileEventResourceComponentHelper;
import org.rhq.core.pluginapi.inventory.ClassLoaderFacet;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
import org.rhq.core.pluginapi.util.FileUtils;
import org.rhq.core.system.ProcessInfo;
import org.rhq.plugins.jslee.jbossas5.ApplicationServerPluginConfigurationProperties;
import org.rhq.plugins.jslee.jbossas5.helper.JBossInstallationInfo;
import org.rhq.plugins.jslee.jbossas5.helper.JBossInstanceInfo;
import org.rhq.plugins.jslee.jbossas5.helper.JBossProperties;
import org.rhq.plugins.jslee.jbossas5.helper.MobicentsJSleeProperties;
import org.rhq.plugins.jslee.jbossas5.util.JBossASDiscoveryUtils;
import org.rhq.plugins.jslee.jbossas5.util.JnpConfig;
import org.rhq.plugins.jslee.utils.MBeanServerUtils;
import org.rhq.plugins.jslee.utils.jaas.JBossCallbackHandler;
import org.rhq.plugins.jslee.utils.jaas.JBossConfiguration;

public class JainSleeServerDiscoveryComponent implements ResourceDiscoveryComponent<JainSleeServerComponent>,
        ManualAddFacet<JainSleeServerComponent>, ClassLoaderFacet<JainSleeServerComponent> {

    private final Log log = LogFactory.getLog(this.getClass());

    private static final String CHANGE_ME = "***CHANGE_ME***";
    private static final String ANY_ADDRESS = "0.0.0.0";
    private static final String LOCALHOST = "127.0.0.1";

    private static final String JBOSS_SERVICE_XML = "conf" + File.separator + "jboss-service.xml";
    private static final String JBOSS_NAMING_SERVICE_XML = "deploy" + File.separator + "naming-service.xml";
    private static final String JAVA_HOME_ENV_VAR = "JAVA_HOME";

    private static final String[] CLIENT_JARS = new String[] { "client/jbossall-client.jar",
            "common/lib/jboss-security-aspects.jar", "lib/jboss-managed.jar", "lib/jboss-metatype.jar",
            "lib/jboss-dependency.jar" };

    public Set discoverResources(ResourceDiscoveryContext<JainSleeServerComponent> discoveryContext)
            throws InvalidPluginConfigurationException, Exception {
        log.trace("Discovering Mobicents JSLEE Server 2.0.0.BETA2 and above Resources...");
        Set<DiscoveredResourceDetails> resources = new HashSet<DiscoveredResourceDetails>();
        DiscoveredResourceDetails inProcessJBossAS = discoverInProcessJBossAS(discoveryContext);
        if (inProcessJBossAS != null) {
            // If we're running inside a JBoss AS JVM, that's the only AS instance we want to discover.
            resources.add(inProcessJBossAS);
        } else {
            // Otherwise, scan the process table for external AS instances.
            resources.addAll(discoverExternalJBossAsProcesses(discoveryContext));
        }
        log.trace("Discovered " + resources.size() + " JBossAS 5.x and 6.x Resources.");
        return resources;
    }

    public DiscoveredResourceDetails discoverResource(Configuration pluginConfig,
            ResourceDiscoveryContext<JainSleeServerComponent> context) throws InvalidPluginConfigurationException {

        ProcessInfo processInfo = null;
        String jbossHomeDir = pluginConfig.getSimple(ApplicationServerPluginConfigurationProperties.SERVER_HOME_DIR)
                .getStringValue();

        String mobicentsJSleeHome = jbossHomeDir + File.separator + "deploy" + File.separator + "mobicents-slee";

        File mobicentsSlee = new File(mobicentsJSleeHome);

        try {
            if (!mobicentsSlee.getCanonicalFile().isDirectory()) {
                log.warn("Skipping manuall add for Mobicents JAIN SLEE Server " + mobicentsJSleeHome
                        + ", because Mobicents dir 'mobicents-slee' does not exist or is not a directory.");

            }
        } catch (IOException e) {
            log.error("Skipping discovery for Mobicents JAIN SLEE Server " + mobicentsJSleeHome
                    + ", because Mobicents dir could not be canonicalized.", e);
            throw new InvalidPluginConfigurationException(e);
        }

        pluginConfig.put(new PropertySimple(MobicentsJSleeProperties.JSLEE_HOME_DIR, mobicentsJSleeHome));

        JBossInstallationInfo installInfo;
        try {
            installInfo = new JBossInstallationInfo(new File(jbossHomeDir));
        } catch (IOException e) {
            throw new InvalidPluginConfigurationException(e);
        }

        DiscoveredResourceDetails resourceDetails;
        try {
            resourceDetails = createResourceDetails(context, pluginConfig, processInfo, installInfo);
        } catch (Exception e) {
            throw new InvalidPluginConfigurationException(e);
        }
        return resourceDetails;
    }

    private String getNamingPort(String namingUrl) {
        // Only include the JNP port in the Resource name if its value is not "***CHANGE_ME***".
        String namingPort = null;
        // noinspection ConstantConditions
        int colonIndex = namingUrl.lastIndexOf(':');
        if ((colonIndex != -1) && (colonIndex != (namingUrl.length() - 1))) {
            // NOTE: We assume the JNP URL does not have a trailing slash.
            String port = namingUrl.substring(colonIndex + 1);
            if (!port.equals(CHANGE_ME))
                namingPort = port;
        }

        return namingPort;
    }

    public List getAdditionalClasspathUrls(ResourceDiscoveryContext<JainSleeServerComponent> context,
            DiscoveredResourceDetails details) throws Exception {
        if (log.isTraceEnabled()) {
            log.trace("getAdditionalClasspathUrls(" + context + ", " + details + ") called.");
        }

        Configuration pluginConfig = details.getPluginConfiguration();
        String homeDir = pluginConfig.getSimple(ApplicationServerPluginConfigurationProperties.HOME_DIR)
                .getStringValue();

        List<URL> clientJars = new ArrayList<URL>();

        for (String jarFileName : CLIENT_JARS) {
            File clientJar = new File(homeDir, jarFileName);
            if (!clientJar.exists()) {
                throw new FileNotFoundException("Cannot find [" + clientJar + "] - unable to manage server.");
            }
            if (!clientJar.canRead()) {
                throw new IOException("Cannot read [" + clientJar + "] - unable to manage server.");
            }
            clientJars.add(clientJar.toURI().toURL());
        }

        return clientJars;
    }

    private DiscoveredResourceDetails discoverInProcessJBossAS(ResourceDiscoveryContext discoveryContext) {
        try {
            return null;// new InProcessJBossASDiscovery().discoverInProcessJBossAS(discoveryContext);
        } catch (Throwable t) {
            log.debug(
                    "In-process JBoss AS discovery failed - we are probably not running embedded within JBoss AS.",
                    t);
            return null;
        }
    }

    private Set<DiscoveredResourceDetails> discoverExternalJBossAsProcesses(
            ResourceDiscoveryContext discoveryContext) throws Exception {
        Set<DiscoveredResourceDetails> resources = new HashSet<DiscoveredResourceDetails>();
        List<ProcessScanResult> autoDiscoveryResults = discoveryContext.getAutoDiscoveredProcesses();

        for (ProcessScanResult autoDiscoveryResult : autoDiscoveryResults) {
            ProcessInfo processInfo = autoDiscoveryResult.getProcessInfo();
            if (log.isDebugEnabled())
                log.debug("Discovered JBoss AS process: " + processInfo);

            JBossInstanceInfo cmdLine;
            try {
                cmdLine = new JBossInstanceInfo(processInfo);
            } catch (Exception e) {
                log.error("Failed to process JBoss AS command line: " + Arrays.asList(processInfo.getCommandLine()),
                        e);
                continue;
            }

            // TODO : Make sure its Mobicents 2.0.0.BETA2 and above here, because after this we are looking for folder
            // mobicents-slee
            // Skip it if it's an AS/EAP/SOA-P version we don't support.
            JBossInstallationInfo installInfo = cmdLine.getInstallInfo();
            // if (!isSupportedProduct(installInfo)) {
            // // continue;
            // }

            File installHome = new File(cmdLine.getSystemProperties().getProperty(JBossProperties.HOME_DIR));
            File configDir = new File(cmdLine.getSystemProperties().getProperty(JBossProperties.SERVER_HOME_DIR));

            // The config dir might be a symlink - call getCanonicalFile() to resolve it if so, before
            // calling isDirectory() (isDirectory() returns false for a symlink, even if it points at
            // a directory).
            try {
                if (!configDir.getCanonicalFile().isDirectory()) {
                    log.warn("Skipping discovery for JBoss AS process " + processInfo
                            + ", because configuration dir '" + configDir
                            + "' does not exist or is not a directory.");
                    continue;
                }
            } catch (IOException e) {
                log.error("Skipping discovery for JBoss AS process " + processInfo + ", because configuration dir '"
                        + configDir + "' could not be canonicalized.", e);
                continue;
            }

            File mobicentsSlee = new File(
                    cmdLine.getSystemProperties().getProperty(MobicentsJSleeProperties.JSLEE_HOME_DIR));
            try {
                if (!mobicentsSlee.getCanonicalFile().isDirectory()) {
                    log.warn("Skipping discovery for Mobicents JAIN SLEE process " + processInfo
                            + ", because Mobicents dir " + mobicentsSlee.getCanonicalFile().getAbsolutePath()
                            + " does not exist or is not a directory.");
                    continue;
                }
            } catch (IOException e) {
                log.error("Skipping discovery for Mobicents JAIN SLEE process " + processInfo
                        + ", because Mobicents dir could not be canonicalized.", e);
                continue;
            }

            Configuration pluginConfiguration = discoveryContext.getDefaultPluginConfiguration();

            String jnpURL = getJnpURL(cmdLine, installHome, configDir);

            // TODO? Set the connection type - local or remote

            // Set the required props...
            pluginConfiguration
                    .put(new PropertySimple(ApplicationServerPluginConfigurationProperties.NAMING_URL, jnpURL));
            pluginConfiguration.put(new PropertySimple(ApplicationServerPluginConfigurationProperties.HOME_DIR,
                    installHome.getAbsolutePath()));
            pluginConfiguration.put(
                    new PropertySimple(ApplicationServerPluginConfigurationProperties.SERVER_HOME_DIR, configDir));

            pluginConfiguration.put(new PropertySimple(MobicentsJSleeProperties.JSLEE_HOME_DIR,
                    cmdLine.getSystemProperties().getProperty(MobicentsJSleeProperties.JSLEE_HOME_DIR)));

            // Set the optional props...
            pluginConfiguration.put(new PropertySimple(ApplicationServerPluginConfigurationProperties.SERVER_NAME,
                    cmdLine.getSystemProperties().getProperty(JBossProperties.SERVER_NAME)));
            pluginConfiguration.put(new PropertySimple(ApplicationServerPluginConfigurationProperties.BIND_ADDRESS,
                    cmdLine.getSystemProperties().getProperty(JBossProperties.BIND_ADDRESS)));

            pluginConfiguration
                    .put(new PropertySimple(ApplicationServerPluginConfigurationProperties.SERVER_TMP_DIR,
                            cmdLine.getSystemProperties().getProperty(JBossProperties.SERVER_TEMP_DIR)));
            JBossASDiscoveryUtils.UserInfo userInfo = JBossASDiscoveryUtils.getJmxInvokerUserInfo(configDir);
            if (userInfo != null) {
                pluginConfiguration.put(new PropertySimple(ApplicationServerPluginConfigurationProperties.PRINCIPAL,
                        userInfo.getUsername()));
                pluginConfiguration.put(new PropertySimple(
                        ApplicationServerPluginConfigurationProperties.CREDENTIALS, userInfo.getPassword()));
            }

            String javaHome = processInfo.getEnvironmentVariable(JAVA_HOME_ENV_VAR);
            if (javaHome == null && log.isDebugEnabled()) {
                log.debug("JAVA_HOME environment variable not set in JBoss AS process - defaulting "
                        + ApplicationServerPluginConfigurationProperties.JAVA_HOME
                        + " connection property to the plugin container JRE dir.");
                javaHome = System.getenv(JAVA_HOME_ENV_VAR);
            }

            pluginConfiguration
                    .put(new PropertySimple(ApplicationServerPluginConfigurationProperties.JAVA_HOME, javaHome));

            initLogEventSourcesConfigProp(configDir, pluginConfiguration);

            // TODO: Init props that have static defaults.
            // setPluginConfigurationDefaults(pluginConfiguration);

            DiscoveredResourceDetails resourceDetails = createResourceDetails(discoveryContext, pluginConfiguration,
                    processInfo, installInfo);
            resources.add(resourceDetails);
        }
        return resources;
    }

    private DiscoveredResourceDetails createResourceDetails(ResourceDiscoveryContext discoveryContext,
            Configuration pluginConfig, ProcessInfo processInfo, JBossInstallationInfo installInfo)
            throws Exception {
        String serverHomeDir = pluginConfig.getSimple(MobicentsJSleeProperties.JSLEE_HOME_DIR).getStringValue();
        File absoluteConfigPath = resolvePathRelativeToHomeDir(pluginConfig, serverHomeDir);

        // Canonicalize the config path, so it's consistent no matter how it's entered.
        // This prevents two servers with different forms of the same config path, but
        // that are actually the same server, from ending up in inventory.
        // JON: fix for JBNADM-2634 - do not resolve symlinks (ips, 12/18/07)
        String key = FileUtils.getCanonicalPath(absoluteConfigPath.getPath());

        String bindAddress = pluginConfig.getSimple(ApplicationServerPluginConfigurationProperties.BIND_ADDRESS)
                .getStringValue();
        String namingUrl = pluginConfig.getSimple(ApplicationServerPluginConfigurationProperties.NAMING_URL)
                .getStringValue();

        // Only include the JNP port in the Resource name if its value is not "***CHANGE_ME***".
        String namingPort = getNamingPort(namingUrl);

        ObjectName sleemanagement = new ObjectName(SleeManagementMBean.OBJECT_NAME);

        String principal = pluginConfig.getSimple("principal").getStringValue();
        String credentials = pluginConfig.getSimple("credentials").getStringValue();

        MBeanServerUtils mbeanUtils = new MBeanServerUtils(namingUrl, principal, credentials);
        MBeanServerConnection connection = mbeanUtils.getConnection();

        try {
            mbeanUtils.login();

            String sleeName = (String) connection.getAttribute(sleemanagement, "SleeName");
            String sleeVersion = (String) connection.getAttribute(sleemanagement, "SleeVersion");
            String sleeVendor = (String) connection.getAttribute(sleemanagement, "SleeVendor");

            String description = sleeName + " v" + sleeVersion + " by " + sleeVendor;

            // TODO : DO we care if its RHQ Server?
            // File deployDir = new File(absoluteConfigPath, "deploy");
            // File rhqInstallerWar = new File(deployDir, "rhq-installer.war");
            // File rhqInstallerWarUndeployed = new File(deployDir, "rhq-installer.war.rej");
            // boolean isRhqServer = rhqInstallerWar.exists() || rhqInstallerWarUndeployed.exists();
            // if (isRhqServer) {
            // baseName += " RHQ Server, ";
            // description += " hosting the RHQ Server";
            // // We know this is an RHQ Server. Let's add an event source for its server log file, but disable it by
            // // default.
            // configureEventSourceForServerLogFile(pluginConfig);
            // }
            String name = formatServerName(sleeName + " " + sleeVersion, bindAddress, namingPort);

            return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, sleeVersion,
                    description, pluginConfig, processInfo);
        } finally {
            try {
                mbeanUtils.logout();
            } catch (LoginException e) {
                if (log.isDebugEnabled()) {
                    log.debug("Failed to logout from secured JMX", e);
                }
            }
        }
    }

    private String formatServerName(String baseName, String bindingAddress, String jnpPort) {
        String details = null;

        if ((bindingAddress != null) && (jnpPort != null && !jnpPort.equals(CHANGE_ME))) {
            details = bindingAddress + ":" + jnpPort;
        } else if ((bindingAddress == null) && (jnpPort != null && !jnpPort.equals(CHANGE_ME))) {
            details = jnpPort;
        } else if (bindingAddress != null) {
            details = bindingAddress;
        }

        return baseName + ((details != null) ? (" (" + details + ")") : "");
    }

    private static File resolvePathRelativeToHomeDir(Configuration pluginConfig, String path) {
        File configDir = new File(path);
        if (!configDir.isAbsolute()) {
            String homeDir = pluginConfig.getSimple(ApplicationServerPluginConfigurationProperties.HOME_DIR)
                    .getStringValue();
            configDir = new File(homeDir, path);
        }
        return configDir;
    }

    private String getJnpURL(JBossInstanceInfo cmdLine, File installHome, File configDir) {
        File jnpServiceUrlFile = new File(configDir, "data/jnp-service.url");
        if (jnpServiceUrlFile.exists() && jnpServiceUrlFile.canRead()) {
            try {
                BufferedReader br = new BufferedReader(new FileReader(jnpServiceUrlFile));
                String jnpUrl = br.readLine();
                if (jnpUrl != null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Read JNP URL from jnp-service.url file: " + jnpUrl);
                    }
                    return jnpUrl;
                }
            } catch (IOException ioe) {
                // Nothing to do
            }
        }

        log.warn("Failed to read JNP URL from '" + jnpServiceUrlFile + "'.");

        // Above did not work, so fall back to our previous scheme
        JnpConfig jnpConfig = getJnpConfig(installHome, configDir, cmdLine.getSystemProperties());
        String jnpAddress = (jnpConfig.getJnpAddress() != null) ? jnpConfig.getJnpAddress() : CHANGE_ME;
        if (ANY_ADDRESS.equals(jnpAddress)) {
            jnpAddress = LOCALHOST;
        }
        String jnpPort = (jnpConfig.getJnpPort() != null) ? String.valueOf(jnpConfig.getJnpPort()) : CHANGE_ME;
        return "jnp://" + jnpAddress + ":" + jnpPort;
    }

    private static JnpConfig getJnpConfig(File installHome, File configDir, Properties props) {
        File serviceXML = new File(configDir, JBOSS_SERVICE_XML);
        JnpConfig config = JnpConfig.getConfig(installHome, serviceXML, props);
        if ((config == null) || (config.getJnpPort() == null)) {
            File namingServiceFile = new File(configDir, JBOSS_NAMING_SERVICE_XML);
            if (namingServiceFile.exists()) {
                config = JnpConfig.getConfig(installHome, namingServiceFile, props);
            }
        }
        return config;
    }

    private void initLogEventSourcesConfigProp(File configDir, Configuration pluginConfig) {
        File logDir = new File(configDir, "log");
        File serverLogFile = new File(logDir, "server.log");
        if (serverLogFile.exists() && !serverLogFile.isDirectory()) {
            PropertyMap serverLogEventSource = new PropertyMap("serverLog");
            serverLogEventSource.put(new PropertySimple(
                    LogFileEventResourceComponentHelper.LogEventSourcePropertyNames.LOG_FILE_PATH, serverLogFile));
            serverLogEventSource.put(new PropertySimple(
                    LogFileEventResourceComponentHelper.LogEventSourcePropertyNames.ENABLED, Boolean.FALSE));
            PropertyList logEventSources = pluginConfig
                    .getList(LogFileEventResourceComponentHelper.LOG_EVENT_SOURCES_CONFIG_PROP);

            // TODO : Not sure why I get this null
            if (logEventSources != null) {
                logEventSources.add(serverLogEventSource);
            }
        }
    }

}