/* * RHQ Management Platform * Copyright (C) 2005-2008 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.perftest; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.prefs.Preferences; /** * This is a Java program that enables you to launch multiple agents within a single VM. Use this for performance * testing when you want to run alot of agents all hitting a single server. * * <p>See the PROP_XXX constants for the different system properties you can set to control the behavior and setup of * the spawned agents. They have "sensible" defaults, but you'll usually want to set one or more explicitly to define * things such as the number of agents you want to run ({@link #PROP_SPAWNCOUNT}), what the starting port is that you * want the agents to listen to ({@link #PROP_STARTPORT}) and where the agent distribution exists so the spawned agents * can find their dependency jars ({@link #PROP_AGENTDIST}).</p> * * <p>The spawned agents will all have the same base configuration defined by a single agent configuration .xml file. * This Java program is built with a default agent configuration that should ship within its jar so you typically don't * have to create your own agent configuration .xml file. If you do have a .xml file that you want to use as the agents' * base configurations, point the system property {@link #PROP_CONFIGFILE} to the file path where your .xml file * exists.</p> * * <p>You can override any of the base configuration's preferences by setting a system property with the same name as * the preference. This is useful, for example, if you do not want the agents to download and maintain their own copies * of plugins. You can copy all plugins in a single location and tell all spawned agents to share those via: <code> * -Drhq.agent.plugins.directory=/shared-plugins -Drhq.agent.update-plugins-at-startup=false</code>. In fact, that * specific use-case is so useful, setting the system property {@link #PROP_PLUGINSDIR} will set those two properties * for you appropriately.</p> * * @author John Mazzitelli */ public class AgentSpawn { // MAKE SURE THESE MATCH WHAT THE REAL AGENT EXPECTS private static final String PREFERENCE_NODE_PARENT = "rhq-agent"; private static final String PREF_SCHEMA_VERSION = "rhq.agent.configuration-schema-version"; private static final String PREF_AGENT_NAME = "rhq.agent.name"; private static final String PREF_AGENT_BIND_ADDRESS = "rhq.communications.connector.bind-address"; private static final String PREF_AGENT_BIND_PORT = "rhq.communications.connector.bind-port"; private static final String PREF_PLUGINS_DIRECTORY = "rhq.agent.plugins.directory"; private static final String PREF_COMM_DATA_DIRECTORY = "rhq.communications.data-directory"; private static final String PREF_AGENT_DATA_DIRECTORY = "rhq.agent.data-directory"; private static final String PREF_MBS_NAME = "rhq.communications.service-container.mbean-server-name"; private static final String PREF_UPDATE_PLUGINS_AT_STARTUP = "rhq.agent.update-plugins-at-startup"; // PERFTEST PROPERTIES THIS CLASS USES TO SPAWN OUR TEST AGENTS private static final String PROP_AGENTDIST = "perftest.agentdist"; private static final String PROP_CONFIGFILE = "perftest.config"; private static final String PROP_PREF = "perftest.pref"; private static final String PROP_SPAWNDIR = "perftest.spawndir"; private static final String PROP_SPAWNCOUNT = "perftest.spawncount"; private static final String PROP_STARTPORT = "perftest.startport"; private static final String PROP_BINDADDRESS = "perftest.bindaddress"; private static final String PROP_CMDLINE = "perftest.cmdline"; private static final String PROP_PLUGINSDIR = "perftest.plugins"; private static final String PROP_STARTPAUSE = "perftest.startpause"; private static final String PROP_PIDFILE = "perftest.pidfile"; public static void main(String[] args) throws Exception { System.out.println("==AGENT SPAWN PERFTEST UTILITY=="); System.out.println("Pid File/Pid: (" + PROP_PIDFILE + ")=" + generatePidFile()); System.out.println("Agent Distribution (" + PROP_AGENTDIST + ")=" + getAgentDistribution()); System.out.println("Configuration File (" + PROP_CONFIGFILE + ")=" + getConfigurationFile()); System.out.println("Preferences Node Name (" + PROP_PREF + ")=" + getTemplatePreferencesNodeName()); System.out.println("Spawned Agents Directory (" + PROP_SPAWNDIR + ")=" + getSpawnDirectory()); System.out.println("# Agents To Be Created (" + PROP_SPAWNCOUNT + ")=" + getSpawnCount()); System.out.println("Start Port Of Agent Listeners (" + PROP_STARTPORT + ")=" + getStartPort()); System.out.println("Agents Command Line Arguments (" + PROP_CMDLINE + ")=" + System.getProperty(PROP_CMDLINE)); System.out.println("Shared Plugins Directory (" + PROP_PLUGINSDIR + ")=" + getSharedPluginsDirectory()); System.out.println("Pause (ms) Between Each Start (" + PROP_STARTPAUSE + ")=" + getStartPause()); Map<String, String> overrideProps = new HashMap<String, String>(); populateOverrideProps(overrideProps); for (Map.Entry<String, String> entry : overrideProps.entrySet()) { System.out.println("Override property: " + entry.getKey() + '=' + entry.getValue()); } if (args.length == 0) { usage(); return; } if (args[0].equals("clean")) { clean(args); } else if (args[0].equals("spawn")) { spawn(); } else if (args[0].equals("start")) { spawn(); start(args); } else { usage(); } } private static void start(String[] args) throws Exception { // so we pick up our own log4j for the spawned agents but allow someone to override it if (System.getProperty("log4j.configuration") == null) { System.setProperty("log4j.configuration", "spawn-log4j.xml"); } final int count = getSpawnCount(); final int startPort = getStartPort(); final long startPause = getStartPause(); final boolean dryrun = ((args.length == 2) && args[1].equals("dryrun")); System.out.println("Starting [" + count + "] agents beginning at port [" + startPort + "]..."); URL[] nativeDirs = getSpawnedAgentNativeLibraryDirectories(); URL[] jniJars = getSpawnedAgentJNIClasspath(); ClassLoader topClassloader = new SpawnedAgentClassLoader(jniJars, nativeDirs, null); for (int i = 0; i < count; i++) { System.out.println("Agent #" + i + ": " + (startPort + i)); // We need to build the agent's classloader so it is completely isolated. URL[] jars = getSpawnedAgentClasspath(); URL[] endorsedJars = getSpawnedAgentEndorsedClasspath(); URL[] allJars = new URL[jars.length + endorsedJars.length]; System.arraycopy(endorsedJars, 0, allJars, 0, endorsedJars.length); System.arraycopy(jars, 0, allJars, endorsedJars.length, jars.length); final ClassLoader agentClassLoader = new SpawnedAgentClassLoader(allJars, null, topClassloader); final int agentId = i; // we need to instantiate and start the agent in its own thread so it can have // a default context classloader that is our isolated classloader Runnable agentRunnable = new Runnable() { public void run() { try { Class<?> agentClass = agentClassLoader.loadClass("org.rhq.enterprise.agent.AgentMain"); Class<?>[] params = new Class[] { String[].class }; Object[] args = getAgentCommandLine(agentId); Constructor<?> agentConstructor = agentClass.getConstructor(params); Method startMethod = agentClass.getMethod("start", new Class[0]); if (!dryrun) { Object agentInstance = agentConstructor.newInstance(new Object[] { args }); startMethod.invoke(agentInstance, new Object[0]); } else { // this is a dry run - just output what we would have done System.out.print("DRYRUN: "); System.out.print(agentConstructor.getName()); System.out.println(" with args " + Arrays.toString(args)); } } catch (Throwable t) { System.out.println("Cannot start agent #" + agentId + " on port " + (startPort + agentId) + ". Cause: " + t); t.printStackTrace(); } } }; // create our thread that starts the agent with the isolated class loader as its context Thread agentThread = new Thread(agentRunnable, "Starting Agent " + (startPort + i)); agentThread.setDaemon(true); agentThread.setContextClassLoader(agentClassLoader); Thread.sleep(startPause); agentThread.start(); if (dryrun) { // in a dryrun, the thread finishes fast, let's wait for it so we output the messages in order agentThread.join(); } } int endPort = (startPort + count - 1); System.out.println("Started " + count + " agents running on ports " + startPort + "-" + endPort); // if this is not a dryrun, let's hang this main thread so we keep all agents alive // effectively, this means you need to forcibly kill this VM when not in a dry run if (!dryrun) { synchronized (AgentSpawn.class) { AgentSpawn.class.wait(); } } return; } private static void spawn() throws Exception { System.out.println("Loading template configuration..."); Preferences templatePrefs = loadConfigurationFile(); System.out.println("Spawning agent configurations..."); spawnAgentConfigurations(templatePrefs); } private static void usage() { System.out.println("Commands: clean | spawn | start"); System.out.println("\tclean [all]: cleans spawned agents' files and preferences,\n" + "\t specify 'all' and this will attempt to clean up\n" + "\t everything it ever created"); System.out.println("\tspawn: spawns the agents files and preferences, but does not start them"); System.out.println("\tstart [dryrun]: spawns and starts the agents\n" + "\t specify 'dryrun' to not really start them\n"); return; } private static void clean(String[] args) throws Exception { System.out.println("Cleaning spawned agents..."); Map<Integer, Preferences> allPrefs = getSpawnedAgentPreferencesNodes(); for (Preferences doomedPrefs : allPrefs.values()) { // even if "all" was specified, let's do this just in case the // data and/or plugin directories were specified somewhere else // other than under the spawn directory (which "all" will scrub) String data = doomedPrefs.get(PREF_AGENT_DATA_DIRECTORY, null); if (data != null) { purge(new File(data), true); } String plugins = doomedPrefs.get(PREF_PLUGINS_DIRECTORY, null); if (plugins != null) { purge(new File(plugins), true); } doomedPrefs.removeNode(); } if ((args.length == 2) && args[1].equals("all")) { System.out.println("Performing a full clean - will attempt to scrub everything..."); Preferences topNode = Preferences.userRoot().node(PREFERENCE_NODE_PARENT); for (String childNode : topNode.childrenNames()) { if (childNode.startsWith("spawn")) { topNode.node(childNode).removeNode(); } } purge(getSpawnDirectory(), true); } return; } private static Map<Integer, Preferences> getSpawnedAgentPreferencesNodes() { Map<Integer, Preferences> allPrefs = new HashMap<Integer, Preferences>(); int count = getSpawnCount(); for (int i = 0; i < count; i++) { // keyed on "agent ID" which is just a number from 0 - (count-1) allPrefs.put(Integer.valueOf(i), getPreferencesNode(i)); } return allPrefs; } private static void spawnAgentConfigurations(Preferences templatePrefs) throws Exception { // first collect our global overrides that may have been defined Map<String, String> overrideProps = new HashMap<String, String>(); // if the agents are to share the same set of plugins, set the proper override properties to enable this String sharedPluginsDir = getSharedPluginsDirectory(); if (sharedPluginsDir != null) { overrideProps.put(PREF_PLUGINS_DIRECTORY, sharedPluginsDir); overrideProps.put(PREF_UPDATE_PLUGINS_AT_STARTUP, "false"); } populateOverrideProps(overrideProps); // create some things on the filesystem that are needed by the spawned agents File spawnDir = getSpawnDirectory(); File data = new File(spawnDir, "data"); // this is the root directory of all our data directories File plugins = new File(spawnDir, "plugins"); // this is the root directory of all our plugins directories data.mkdirs(); plugins.mkdirs(); if (!data.isDirectory()) { throw new RuntimeException("Cannot create root data directory: " + data); } if (!plugins.isDirectory()) { throw new RuntimeException("Cannot create root plugins directory: " + plugins); } // create all the spawned agents' configurations now int count = getSpawnCount(); int startPort = getStartPort(); for (int i = 0; i < count; i++) { // get the spawned agent's config and clear it out of any old config that might still exist Preferences prefs = getPreferencesNode(i); prefs.clear(); // start out by giving the spawned agent the template configuration for (String key : templatePrefs.keys()) { prefs.put(key, templatePrefs.get(key, "**SHOULD NEVER SEE THIS**")); } // now override some configuration settings so they don't conflict with other spawned agents int spawnPort = startPort + i; // this is the port our spawned agent will listen to prefs.put(PREF_AGENT_NAME, getPreferencesNodeName(i)); prefs.put(PREF_AGENT_BIND_ADDRESS, getBindAddress()); prefs.putInt(PREF_AGENT_BIND_PORT, spawnPort); prefs.put(PREF_AGENT_DATA_DIRECTORY, new File(data, Integer.toString(spawnPort)).getAbsolutePath()); prefs.put(PREF_COMM_DATA_DIRECTORY, new File(data, Integer.toString(spawnPort)).getAbsolutePath()); prefs.put(PREF_PLUGINS_DIRECTORY, new File(plugins, Integer.toString(spawnPort)).getAbsolutePath()); prefs.put(PREF_MBS_NAME, getPreferencesNodeName(i)); // if the user specified some overrides, set them now // this is useful if you do not want the agents to download and maintain // their own copies of plugins. You can copy all plugins in a single location // and tell all spawned agents to share those via: // -Drhq.agent.plugins.directory=/shared-plugins // -Drhq.agent.update-plugins-at-startup=false for (Map.Entry<String, String> entry : overrideProps.entrySet()) { prefs.put(entry.getKey(), entry.getValue()); } } } /** * Puts all relevent system properties in the given map. * * @param overrideProps */ private static void populateOverrideProps(Map<String, String> overrideProps) { Properties sysProps = System.getProperties(); for (Map.Entry<Object, Object> entry : sysProps.entrySet()) { if (entry.getKey().toString().startsWith("rhq.")) { overrideProps.put(entry.getKey().toString(), entry.getValue().toString()); } } } /** * Returns the file path where an agent distribution can be found. This is used to location the jars and native * libraries required to run an agent. * * @return the location of the agent distro where all dependency jars can be found * * @throws RuntimeException */ private static String getAgentDistribution() { // default uses the maven build layout and assume we are running out of the perftest target dir String distString = System.getProperty(PROP_AGENTDIST, "../../../modules/enterprise/agent/target/rhq-agent"); File dist = new File(distString); if (!dist.isDirectory()) { throw new RuntimeException("Missing the agent distribution at : " + distString + ". Please set [" + PROP_AGENTDIST + "] to point to the exploded agent distribution (not a zip/tar file)."); } return distString; } /** * The name of the configuration file. This is the template of all spawned agents. A spawned agent will start out * with the configuration found in this file, with certain preferences overridden so they don't conflict with other * agents. This will either be a file path, URL or resource path found on the classpath. * * @return location of the template agent configuration */ private static String getConfigurationFile() { return System.getProperty(PROP_CONFIGFILE, "template-configuration.xml"); } /** * This is the preferences node name of the configuration file. The true name of the spawned agents will be a * derivitive of this name. * * @return configuration file's preferences node name */ private static String getTemplatePreferencesNodeName() { return System.getProperty(PROP_PREF, "spawn"); } /** * The name of the preferences node for the identified spawned agent. This will be a derivitive of * {@link #getTemplatePreferencesNodeName()}. * * @param agentId ID of the spawned agent whose preferences node name is to be returned * * @return the preferences node name of the spawned agent identified by the given ID */ private static String getPreferencesNodeName(int agentId) { return getTemplatePreferencesNodeName() + '_' + (getStartPort() + agentId); } /** * Gets the preferences node of the template configuration as defined in the {@link #getConfigurationFile()}. This * is the base configuration of all spawned agents. Some of these preferences will be overridden so they do not * conflict with other agents. * * @return template configuration preferences */ private static Preferences getTemplatePreferencesNode() { Preferences topNode = Preferences.userRoot().node(PREFERENCE_NODE_PARENT); Preferences preferencesNode = topNode.node(getTemplatePreferencesNodeName()); return preferencesNode; } /** * Gets the preferences node of the identified spawned agent. This consists of the * {@link #getTemplatePreferencesNode() template configuration} with some preferences overridden so they do not * conflict with other agents. * * @param agentId ID of the spawned agent whose preferences are to be returned * * @return spawned agent's preferences */ private static Preferences getPreferencesNode(int agentId) { Preferences topNode = Preferences.userRoot().node(PREFERENCE_NODE_PARENT); Preferences preferencesNode = topNode.node(getPreferencesNodeName(agentId)); return preferencesNode; } private static File getSpawnDirectory() { File dir; String dirString = System.getProperty(PROP_SPAWNDIR); if ((dirString == null) || (dirString.trim().length() == 0)) { String root = null; // System.getProperty("java.io.tmpdir") dir = new File(root, "spawn"); } else { dir = new File(dirString.trim()); } if (!dir.isDirectory()) { dir.mkdirs(); if (!dir.isDirectory()) { throw new RuntimeException("Invalid " + PROP_SPAWNDIR + " : " + dirString); } } return dir; } private static int getSpawnCount() { String countString = System.getProperty(PROP_SPAWNCOUNT, "2"); int count = Integer.parseInt(countString); if (count < 1) { throw new RuntimeException("Invalid " + PROP_SPAWNCOUNT + " : " + countString); } return count; } private static int getStartPort() { String portString = System.getProperty(PROP_STARTPORT, "36163"); int port = Integer.parseInt(portString); if ((port < 1) || (port > 65535)) { throw new RuntimeException("Invalid " + PROP_STARTPORT + " : " + portString); } return port; } /** * This will return the path of the shared plugins directory, if defined. If the spawned agents will not share a set * of plugins, this will return <code>null</code>. If a shared plugins directory was specified, but that directory * does not exist or is invalid, an exception is thrown. * * @return the location where the shared plugins exist (may be <code>null</code> if not defined) */ private static String getSharedPluginsDirectory() { String dir = System.getProperty(PROP_PLUGINSDIR); if ((dir != null) && (dir.trim().length() > 0)) { if (!new File(dir.trim()).isDirectory()) { throw new RuntimeException("Specified plugins directory does not exist: " + dir); } } else { dir = null; } return dir; } /** * The number of milliseconds that should pass before starting an agent. * * @return pause time in milliseconds */ private static long getStartPause() { String timeString = System.getProperty(PROP_STARTPAUSE, "1000"); long time = Long.parseLong(timeString); if (time < 0) { throw new RuntimeException("Invalid " + PROP_STARTPAUSE + " : " + timeString); } return time; } /** * If this AgentSpawn process was told to generate a pid file, it does it now and returns * a string with the name of the pid file and the PID. * * @return string identifying the pid file and the pid of the process */ private static String generatePidFile() { String pidfile = System.getProperty(PROP_PIDFILE); if (pidfile == null) { return "<not generated/unknown pid>"; } // here we assume the MBean name has the pid in it - cross your fingers try { String name = ManagementFactory.getRuntimeMXBean().getName(); int endIndex = name.indexOf("@"); int pid = Integer.parseInt(name.substring(0, endIndex)); FileOutputStream fos = new FileOutputStream(new File(pidfile)); fos.write(Integer.toString(pid).getBytes()); fos.close(); return pidfile + " (" + pid + ")"; } catch (Throwable t) { return "<cannot determine pid: " + t + ">"; } } /** * Note that the command line arguments should be separated by a comma. * * @param agentId * * @return the command line that should be used to pass to the identified agent's main method */ private static String[] getAgentCommandLine(int agentId) { String cmdline = System.getProperty(PROP_CMDLINE); String[] arguments; if ((cmdline == null) || (cmdline.trim().length() == 0)) { arguments = new String[0]; } else { arguments = cmdline.trim().split(","); } ArrayList<String> argsList = new ArrayList<String>(); for (String argument : arguments) { argsList.add(argument); } String prefs = getPreferencesNodeName(agentId); argsList.add("--daemon"); argsList.add("--pref"); argsList.add(prefs); argsList.add("--output"); argsList.add(new File(getSpawnDirectory(), "logs/" + prefs + ".out").getAbsolutePath()); return argsList.toArray(new String[argsList.size()]); } private static String getBindAddress() { String bindAddress = System.getProperty(PROP_BINDADDRESS); if ((bindAddress == null) || (bindAddress.trim().length() == 0)) { try { bindAddress = InetAddress.getLocalHost().getHostAddress(); } catch (Exception e) { bindAddress = "127.0.0.1"; } } return bindAddress.trim(); } /** * Loads the {@link #getConfigurationFile() configuration file}. The file location will first be checked for * existence on the file system and then as a URL. If it cannot be found, it will be assumed the file location * specifies the file as found in the current class loader and the file will be searched there. An exception is * thrown if the file cannot be found anywhere. * * @return the configuration that was loaded * * @throws Exception if can't load */ private static Preferences loadConfigurationFile() throws Exception { String fileName = getConfigurationFile(); String preferencesNodeName = getTemplatePreferencesNodeName(); InputStream configFileInputStream = null; // first see if the file was specified as a path on the local file system try { File configFile = new File(fileName); if (configFile.exists()) { configFileInputStream = new FileInputStream(configFile); } } catch (Exception e) { // isn't really an error - this just isn't a file on the local file system } // see if the file was specified as a URL if (configFileInputStream == null) { try { URL configFile = new URL(fileName); configFileInputStream = configFile.openStream(); } catch (Exception e) { // isn't really an error - this just isn't a URL } } // if neither a file path or URL, assume the config file can be found in the classloader if (configFileInputStream == null) { configFileInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); } if (configFileInputStream == null) { throw new Exception("Bad config file: " + fileName); } // We need to clear out any previous configuration in case the current config file doesn't specify a preference // that already exists in the preferences node. In this case, the configuration file wants to fall back on the // default value and if we don't clear the preferences, we aren't guaranteed the value stored in the backing // store is the default value. // But first we need to backup these original preferences in case the config file fails to load - // we'll restore the original values in that case. Preferences preferencesNode = getTemplatePreferencesNode(); ByteArrayOutputStream backup = new ByteArrayOutputStream(); preferencesNode.exportSubtree(backup); preferencesNode.clear(); // now load in the preferences try { Preferences.importPreferences(configFileInputStream); if (preferencesNode.getInt(PREF_SCHEMA_VERSION, 0) == 0) { throw new IllegalArgumentException("Bad node name: " + preferencesNodeName); } } catch (Exception e) { // a problem occurred importing the config file; let's restore our original values try { Preferences.importPreferences(new ByteArrayInputStream(backup.toByteArray())); } catch (Exception e1) { // its conceivable the same problem occurred here as with the original exception (backing store // problem?) let's throw the original exception, not this one } throw e; } return preferencesNode; } private static void purge(File dir, boolean deleteIt) { if (dir != null) { if (dir.isDirectory()) { File[] doomedFiles = dir.listFiles(); if (doomedFiles != null) { for (File doomedFile : doomedFiles) { purge(doomedFile, true); // call this method recursively } } } if (deleteIt) { dir.delete(); } } return; } /** * This will return URLs to all of the jars and resources that the spawned agents will have access to in their * isolated classloader. Only those classes within these jars will be accessible to the agent. * * <p>The returns array will <b>not</b> include any JNI jars - they should be in a separate classpath which will be * in an upper classloader.</p> * * @return URLs to embedded agent jars that will be in the embedded agent's classloader * * @throws Exception if failed to find the jars */ private static URL[] getSpawnedAgentClasspath() throws Exception { File dist = new File(getAgentDistribution()); File lib = new File(dist, "lib"); if (!lib.isDirectory()) { throw new Exception("There is no lib directory under [" + dist + "]; cannot get agent jars"); } File[] jarFiles = lib.listFiles(); ArrayList<URL> classpathUrls = new ArrayList<URL>(jarFiles.length + 1); classpathUrls.add(dist.toURI().toURL()); // allows the agent to find resources in here, like log4j.xml for (File jarFile : jarFiles) { if (!jarFile.isDirectory()) { if (!isJNIJar(jarFile)) { classpathUrls.add(jarFile.toURI().toURL()); } } } return classpathUrls.toArray(new URL[classpathUrls.size()]); } /** * This will return URLs to all of the endorsed jars that the spawned agents will have access to in their * isolated classloader. * * <p>The returns array will <b>not</b> include any JNI jars - they should be in a separate classpath which will be * in an upper classloader.</p> * * @return URLs to embedded agent jars that will be in the embedded agent's classloader * * @throws Exception if failed to find the jars */ private static URL[] getSpawnedAgentEndorsedClasspath() throws Exception { File dist = new File(getAgentDistribution()); File lib = new File(dist, "lib/endorsed"); if (!lib.isDirectory()) { throw new Exception("There is no lib/endorsed directory under [" + dist + "]; cannot get endorsed jars"); } File[] jarFiles = lib.listFiles(); ArrayList<URL> endorsedUrls = new ArrayList<URL>(jarFiles.length + 1); //classpathUrls.add(dist.toURI().toURL()); // allows the agent to find resources in here, like log4j.xml for (File jarFile : jarFiles) { if (!jarFile.isDirectory()) { if (!isJNIJar(jarFile)) { endorsedUrls.add(jarFile.toURI().toURL()); } } } return endorsedUrls.toArray(new URL[endorsedUrls.size()]); } /** * This will return URLs to all of the jars containing our JNI classes. * * @return URLs to jars with our JNI classes * * @throws Exception if failed to find the jars */ private static URL[] getSpawnedAgentJNIClasspath() throws Exception { File dist = new File(getAgentDistribution()); File lib = new File(dist, "lib"); if (!lib.isDirectory()) { throw new Exception("There is no lib directory under [" + dist + "]; cannot get JNI jars"); } File[] jarFiles = lib.listFiles(); ArrayList<URL> classpathUrls = new ArrayList<URL>(jarFiles.length + 1); for (File jarFile : jarFiles) { if (isJNIJar(jarFile)) { classpathUrls.add(jarFile.toURI().toURL()); } } return classpathUrls.toArray(new URL[classpathUrls.size()]); } private static boolean isJNIJar(File jarFile) { String name = jarFile.getName(); if (name.startsWith("sigar") && name.endsWith(".jar")) { return true; } return false; } private static URL[] getSpawnedAgentNativeLibraryDirectories() throws Exception { String dist = getAgentDistribution(); ArrayList<URL> nativeLibraryDirUrls = new ArrayList<URL>(); // we will assume our native libraries are under the same lib directory where the jars are // we will also look for native libraries in subdirectories under lib File lib = new File(dist, "lib"); if (!lib.isDirectory()) { throw new Exception("There is no lib directory under [" + dist + "]; cannot get native library directories"); } addDirectories(lib, nativeLibraryDirUrls); return nativeLibraryDirUrls.toArray(new URL[nativeLibraryDirUrls.size()]); } private static void addDirectories(File dir, ArrayList<URL> list) throws Exception { if (dir.isDirectory()) { // add the given directory to the list and add all its subdirectories to the list list.add(dir.toURI().toURL()); for (File dirEntry : dir.listFiles()) { addDirectories(dirEntry, list); } } return; } }