/* * 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 as published by * the Free Software Foundation version 2 of the License. * * 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 for more details. * * You should have received a copy of the GNU 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.jbossas5.helper; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Map; import java.util.Properties; import gnu.getopt.Getopt; import gnu.getopt.LongOpt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.system.ProcessInfo; import org.rhq.core.util.StringPropertyReplacer; import org.rhq.plugins.jbossas5.util.JBossConfigurationUtility; /** * Information about a running JBossAS process. System properties, other JVM arguments, JBoss properties, and other * JBossAS arguments are parsed as <code>org.jboss.Main</code> would parse them, and exposed via getters. * * @author Ian Springer */ public class JBossInstanceInfo { private static final Log LOG = LogFactory.getLog(JBossInstanceInfo.class); private static final String JBOSS_MAIN_CLASS_NAME = "org.jboss.Main"; private static final String ANY_ADDRESS = "0.0.0.0"; private final ProcessInfo processInfo; private final Properties sysProps = new Properties(); private String[] classPath; private JBossInstallationInfo installInfo; /** * Parses the command line of a JBossAS process. * * @param processInfo the process info for the JBossAS process * @throws Exception if the process does not appear to be a JBossAS process or if the JBoss home dir cannot be * determined */ public JBossInstanceInfo(ProcessInfo processInfo) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Parsing JBossAS command line " + Arrays.asList(processInfo.getCommandLine()) + "..."); } this.processInfo = processInfo; int i; String[] args = this.processInfo.getCommandLine(); Map<String, String> envVars = this.processInfo.getEnvironmentVariables(); for (i = 0; i < args.length; i++) { if (args[i].equals(JBOSS_MAIN_CLASS_NAME)) { break; } } String[] jvmArgs = new String[i + 1]; System.arraycopy(args, 0, jvmArgs, 0, i + 1); processJvmArgs(jvmArgs); String[] jbossArgs = new String[args.length - jvmArgs.length]; System.arraycopy(args, i + 1, jbossArgs, 0, jbossArgs.length); processJBossArgs(processInfo.priorSnaphot().getCurrentWorkingDirectory(), jbossArgs); finalizeSysProps(); printSysProps(args); } public Properties getSystemProperties() { return this.sysProps; } public JBossInstallationInfo getInstallInfo() { return this.installInfo; } private void processJvmArgs(String args[]) throws Exception { // NOTE: We can't use GNU GetOpt to process the JVM options, because they are not GNU-style options. for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.equals("-cp") || arg.equals("-classpath")) { if (i == args.length - 1) { LOG.error("'" + arg + "' option has no value."); continue; } this.classPath = args[i + 1].split(File.pathSeparator); } else if (arg.startsWith("-D")) { addPropArgToProps(arg.substring("-D".length()), this.sysProps); } } } private void processJBossArgs(String currentWorkingDir, String args[]) { String programName = this.sysProps.getProperty("program.name", "jboss"); String shortOpts = "-:b:c:D:P:"; LongOpt longOpts[] = { new LongOpt("configuration", 1, null, 'c'), new LongOpt("properties", 1, null, 'P'), new LongOpt("host", 1, null, 'b'), }; Getopt options = new Getopt(programName, args, shortOpts, longOpts); // Tell Getopt not to complain to stderr about unrecognized options... options.setOpterr(false); int c; while ((c = options.getopt()) != -1) { switch (c) { case 'b': // 'b' (--host) { String arg = options.getOptarg(); this.sysProps.setProperty(JBossProperties.BIND_ADDRESS, arg); break; } case 'c': // 'c' (--configuration) { String arg = options.getOptarg(); this.sysProps.setProperty(JBossProperties.SERVER_NAME, arg); break; } case 'D': // 'D' { String arg = options.getOptarg(); String name = addPropArgToProps(arg, this.sysProps); String value = this.sysProps.getProperty(name); if (value.equals("")) { // unlike the JVM, org.jboss.Main interprets -Dfoo as foo="true", rather than as foo="" this.sysProps.setProperty(name, Boolean.TRUE.toString()); } break; } case 'P': // 'P' (--properties) { String arg = options.getOptarg(); // cwd can be null if native support (i.e. SIGAR) is not able to determine it. if (!new File(arg).isAbsolute() && currentWorkingDir == null) { LOG.error("Could not determine current working directory. Failed to parse relative path argument to --properties option: " + options.getOptarg()); break; } URL url; try { File workingDir = new File(currentWorkingDir); url = JBossConfigurationUtility.makeURL(arg, workingDir); } catch (Exception e) { LOG.error("Failed to parse argument to --properties option: " + options.getOptarg() + ": " + e); break; } Properties props = new Properties(); InputStream inputStream = null; try { inputStream = new BufferedInputStream(url.openConnection().getInputStream()); props.load(inputStream); } catch (IOException e) { LOG.error("Could not read properties from file: " + arg, e); break; } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { LOG.error("Failed to close properties file: " + arg, e); // not fatal - continue processing... } } } for (Object nameObj : props.keySet()) { String name = (String) nameObj; String value = props.getProperty(name); String newValue = StringPropertyReplacer.replaceProperties(value, this.sysProps); this.sysProps.setProperty(name, newValue); } break; } } } } private static String addPropArgToProps(String arg, Properties props) { int i = arg.indexOf("="); String name; String value; if (i == -1) { name = arg; value = ""; } else { name = arg.substring(0, i); value = arg.substring(i + 1, arg.length()); } props.setProperty(name, value); return name; } /** * Returns the home directory of the JBossAS server - this is the base directory in which you can find bin/ server/ * lib/ client/ docs/ directories. * * @return a File object pointing to the home directory * @throws Exception If there is no way to obtain that directory. */ private File getHomeDir() throws Exception { String currentWorkingDirectory = null; if (processInfo != null) { this.processInfo.priorSnaphot().getCurrentWorkingDirectory(); } File runJar; File binDir; File homeDir; // method 1: should work 99% of the time. if (currentWorkingDirectory != null) { homeDir = new File(currentWorkingDirectory).getParentFile(); binDir = new File(homeDir, "bin"); runJar = new File(binDir, "run.jar"); if (runJar.exists()) { return homeDir; } } // method 2: more expensive, but also more reliable. for (String pathElement : this.classPath) { if (pathElement.endsWith("run.jar")) { runJar = new File(pathElement); if (!runJar.isAbsolute() && !runJar.exists() && currentWorkingDirectory != null) { runJar = new File(currentWorkingDirectory, runJar.getPath()); } if (!runJar.exists()) { break; } binDir = runJar.getParentFile(); if (binDir == null || !binDir.exists()) { break; } homeDir = binDir.getParentFile(); if (homeDir == null || !homeDir.exists()) { break; } return homeDir; } } throw new Exception("Could not determine JBossAS home dir - classpath is: " + Arrays.toString(this.classPath)); } private void finalizeSysProps() throws Exception { File homeDir; if (this.sysProps.containsKey(JBossProperties.HOME_DIR)) { homeDir = new File(this.sysProps.getProperty(JBossProperties.HOME_DIR)); } else { homeDir = getHomeDir(); this.sysProps.setProperty(JBossProperties.HOME_DIR, homeDir.toString()); } if (!this.sysProps.containsKey(JBossProperties.HOME_URL)) { this.sysProps.setProperty(JBossProperties.HOME_URL, homeDir.toURL().toString()); } File serverBaseDir; if (!sysProps.containsKey(JBossProperties.SERVER_BASE_DIR)) { serverBaseDir = new File(homeDir, "server"); this.sysProps.setProperty(JBossProperties.SERVER_BASE_DIR, serverBaseDir.toString()); } else { serverBaseDir = new File(this.sysProps.getProperty(JBossProperties.SERVER_BASE_DIR)); } if (!this.sysProps.containsKey(JBossProperties.SERVER_BASE_URL)) { this.sysProps.setProperty(JBossProperties.SERVER_BASE_URL, serverBaseDir.toURL().toString()); } this.installInfo = new JBossInstallationInfo(homeDir); if (!this.sysProps.containsKey(JBossProperties.SERVER_NAME)) { this.sysProps.setProperty(JBossProperties.SERVER_NAME, this.installInfo.getProductType().DEFAULT_CONFIG_NAME); } String serverName = this.sysProps.getProperty(JBossProperties.SERVER_NAME); if (!this.sysProps.containsKey(JBossProperties.SERVER_HOME_DIR)) { this.sysProps.setProperty(JBossProperties.SERVER_HOME_DIR, serverBaseDir + File.separator + serverName); } if (!this.sysProps.containsKey(JBossProperties.SERVER_HOME_URL)) { String serverHomeDir = this.sysProps.getProperty(JBossProperties.SERVER_HOME_DIR); this.sysProps.setProperty(JBossProperties.SERVER_HOME_URL, new File(serverHomeDir).toURL().toString()); } if (!this.sysProps.containsKey(JBossProperties.BIND_ADDRESS)) { this.sysProps.setProperty(JBossProperties.BIND_ADDRESS, this.installInfo.getDefaultBindAddress()); } String bindAddress = this.sysProps.getProperty(JBossProperties.BIND_ADDRESS); // this will not be null String remoteAddress = getRemoteAddress(bindAddress); String jgroupsBindAddress = this.sysProps.getProperty(JBossProperties.JGROUPS_BIND_ADDRESS); String jgroupsBindAddr = this.sysProps.getProperty(JBossProperties.JGROUPS_BIND_ADDR); if (jgroupsBindAddress == null) { jgroupsBindAddress = (jgroupsBindAddr != null) ? jgroupsBindAddr : remoteAddress; this.sysProps.setProperty(JBossProperties.JGROUPS_BIND_ADDRESS, jgroupsBindAddress); } if (jgroupsBindAddr == null) { this.sysProps.setProperty(JBossProperties.JGROUPS_BIND_ADDR, jgroupsBindAddress); } if (!this.sysProps.contains(JavaSystemProperties.JAVA_RMI_SERVER_HOSTNAME)) { this.sysProps.setProperty(JavaSystemProperties.JAVA_RMI_SERVER_HOSTNAME, remoteAddress); } } private void printSysProps(String[] commandLine) { if (LOG.isDebugEnabled()) { printSysProp(JBossProperties.HOME_DIR); printSysProp(JBossProperties.SERVER_HOME_DIR); printSysProp(JBossProperties.SERVER_NAME); printSysProp(JBossProperties.BIND_ADDRESS); } } private void printSysProp(String name) { String value = this.sysProps.getProperty(name); if (LOG.isDebugEnabled()) { LOG.debug(name + "=" + ((value != null) ? "\"" + value + "\"" : null)); } } /** * Get the remote address corresponding to the given server address. * <p/> * If we pass the address to the client we don't want to tell it to connect to 0.0.0.0, use our host name instead. * * @param address the passed address * @return the remote address */ private static String getRemoteAddress(String address) { try { if (address == null || address.equals(ANY_ADDRESS)) return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException ignored) { } return address; } @Override public String toString() { StringBuilder buf = new StringBuilder(this.getClass().getSimpleName()); buf.append("["); buf.append("sysProps").append("=").append(this.sysProps).append(", "); buf.append("installInfo").append("=").append(this.installInfo); buf.append("]"); return buf.toString(); } }