/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) 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, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.bootstrap;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
/**
* Bootstrap application for starting OpenNMS.
*/
public class Bootstrap {
protected static final String BOOT_PROPERTIES_NAME = "bootstrap.properties";
protected static final String RRD_PROPERTIES_NAME = "rrd-configuration.properties";
protected static final String LIBRARY_PROPERTIES_NAME = "libraries.properties";
protected static final String OPENNMS_HOME_PROPERTY = "opennms.home";
/**
* Matches any file that is a directory.
*/
private static FileFilter m_dirFilter = new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory();
}
};
/**
* Matches any file that has a name ending in ".jar".
*/
private static FilenameFilter m_jarFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
};
/**
* Create a ClassLoader with the JARs found in dirStr.
*
* @param dirStr
* List of directories to search for JARs, separated by
* {@link java.io.File#pathSeparator File.pathSeparator}
* @param recursive
* Whether to recurse into subdirectories of the directories in
* dirStr
* @param append TODO
* @returns A new ClassLoader containing the found JARs
* @return a {@link java.lang.ClassLoader} object.
* @throws java.net.MalformedURLException if any.
*/
public static ClassLoader loadClasses(String dirStr, boolean recursive, boolean append) throws MalformedURLException {
LinkedList<URL> urls = new LinkedList<URL>();
if (append) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (final URL u : ((URLClassLoader) classLoader).getURLs()) {
urls.add(u);
}
}
StringTokenizer toke = new StringTokenizer(dirStr, File.pathSeparator);
while (toke.hasMoreTokens()) {
String token = toke.nextToken();
loadClasses(new File(token), recursive, urls);
}
return newClassLoader(urls);
}
/**
* Create a ClassLoader with the JARs found in dir.
*
* @param dir
* Directory to search for JARs
* @param recursive
* Whether to recurse into subdirectories of dir
* @returns A new ClassLoader containing the found JARs
* @return a {@link java.lang.ClassLoader} object.
* @throws java.net.MalformedURLException if any.
*/
public static ClassLoader loadClasses(File dir, boolean recursive)
throws MalformedURLException {
LinkedList<URL> urls = new LinkedList<URL>();
loadClasses(dir, recursive, urls);
return newClassLoader(urls);
}
/**
* Create a ClassLoader with the list of URLs found in urls.
*
* @param urls
* List of URLs to add to the ClassLoader's search list.
* @returns A new ClassLoader with the specified search list
* @return a {@link java.lang.ClassLoader} object.
*/
public static ClassLoader newClassLoader(LinkedList<URL> urls) {
URL[] urlsArray = urls.toArray(new URL[0]);
return URLClassLoader.newInstance(urlsArray);
}
/**
* Add JARs found in dir to the LinkedList urls.
*
* @param dir
* Directory to search for JARs
* @param recursive
* Whether to recurse into subdirectories of the directory in
* dir
* @param urls
* LinkedList to append found JARs onto
* @throws java.net.MalformedURLException if any.
*/
public static void loadClasses(File dir, boolean recursive, LinkedList<URL> urls) throws MalformedURLException {
// Add the directory
urls.add(dir.toURI().toURL());
if (recursive) {
// Descend into sub-directories
File[] dirlist = dir.listFiles(m_dirFilter);
if (dirlist != null) {
Arrays.sort(dirlist);
for (File childDir : dirlist) {
loadClasses(childDir, recursive, urls);
}
}
}
// Add individual JAR files
File[] children = dir.listFiles(m_jarFilter);
if (children != null) {
Arrays.sort(children);
for (File childFile : children) {
urls.add(childFile.toURI().toURL());
}
}
}
/**
* Determine the OpenNMS home directory based on the location of the JAR
* file containing this code. Finds the JAR file containing this code, and
* if it is found, the file name of the JAR (e.g.: opennms_bootstrap.jar)
* and its parent directory (e.g.: the lib directory) are removed from the
* path and the resulting path (e.g.: /opt/OpenNMS) is returned.
*
* @return Home directory or null if it couldn't be found
*/
public static File findOpenNMSHome() {
ClassLoader l = Thread.currentThread().getContextClassLoader();
try {
String classFile = Bootstrap.class.getName().replace('.', '/') + ".class";
URL url = l.getResource(classFile);
if (url.getProtocol().equals("jar")) {
URL subUrl = new URL(url.getFile());
if (subUrl.getProtocol().equals("file")) {
String filePath = subUrl.getFile();
int i = filePath.lastIndexOf('!');
File file = new File(filePath.substring(0, i));
return file.getParentFile().getParentFile();
}
}
} catch (MalformedURLException e) {
return null;
}
return null;
}
/**
* Copy properties from a property input stream to the system properties.
* Specific properties are copied from the given InputStream.
*
* @param is
* InputStream of the properties file to load.
*/
protected static void loadProperties(InputStream is) throws IOException {
Properties p = new Properties();
p.load(is);
for (Map.Entry<Object, Object> entry : p.entrySet()) {
String propertyName = entry.getKey().toString();
Object value = entry.getValue();
if (value != null) {
System.setProperty(propertyName, value.toString());
}
}
}
/**
* Copy properties from a property file to the system properties.
*/
protected static boolean loadProperties(File f) throws IOException {
if (!f.exists()) {
return false;
}
InputStream is = null;
try {
is = new FileInputStream(f);
loadProperties(is);
return true;
}
finally {
if (is != null) {
is.close();
}
}
}
/**
* Load default properties from the specified OpenNMS home into the
* system properties.
* @param opennmsHome the OpenNMS home directory
* @return whether the property file was able to be loaded into the System properties
* @throws IOException
*/
protected static boolean loadDefaultProperties(File opennmsHome) throws IOException {
boolean propertiesLoaded = true;
File etc = new File(opennmsHome, "etc");
File bootstrapFile = new File(etc, BOOT_PROPERTIES_NAME);
loadProperties(bootstrapFile);
File rrdFile = new File(etc, RRD_PROPERTIES_NAME);
loadProperties(rrdFile);
File libraryFile = new File(etc, LIBRARY_PROPERTIES_NAME);
if (!loadProperties(libraryFile)) {
propertiesLoaded = false;
}
return propertiesLoaded;
}
/**
* Bootloader main method. Takes the following steps to initialize a
* ClassLoader, set properties, and start OpenNMS:
* <ul>
* <li>Checks for existence of opennms.home system property, and loads
* properties file located at ${opennms.home}/etc/bootstrap.properties if
* it exists.</li>
* <li>Calls {@link #findOpenNMSHome findOpenNMSHome} to determine the
* OpenNMS home directory if the bootstrap.properties file has not yet
* been loaded. Sets the opennms.home system property to the path returned
* from findOpenNMSHome.</li>
* <li>Calls {@link #loadClasses(String, boolean, boolean) loadClasses} to create
* a new ClassLoader. ${opennms.home}/etc and ${opennms.home}/lib are
* passed to loadClasses.</li>
* <li>Determines the proper default value for configuration options when
* overriding system properties have not been set. Below are the default
* values.
* <ul>
* <li>opennms.library.jicmp:
* ClassLoader.getResource(System.mapLibraryName("jicmp"))</li>
* <li>opennms.library.jrrd:
* ClassLoader.getResource(System.mapLibraryName("jrrd"))</li>
* <li>log4j.configuration: "log4j.properties"</li>
* <li>jcifs.properties: ClassLoader.getResource("jcifs.properties")</li>
* </ul>
* </li>
* <li>Finally, the main method of org.opennms.netmgt.vmmgr.Controller is
* invoked with the parameters passed in argv.</li>
* </ul>
*
* @param args
* Command line arguments
* @throws java.lang.Exception if any.
*/
public static void main(String[] args) throws Exception {
loadDefaultProperties();
final String classToExec = System.getProperty("opennms.manager.class", "org.opennms.netmgt.vmmgr.Controller");
final String classToExecMethod = "main";
final String[] classToExecArgs = args;
executeClass(classToExec, classToExecMethod, classToExecArgs, false);
}
protected static void executeClass(final String classToExec, final String classToExecMethod, final String[] classToExecArgs, boolean appendClasspath) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException {
String dir = System.getProperty("opennms.classpath");
if (dir == null) {
dir = System.getProperty(OPENNMS_HOME_PROPERTY) + File.separator
+ "classes" + File.pathSeparator
+ System.getProperty(OPENNMS_HOME_PROPERTY) + File.separator
+ "lib" + File.pathSeparator
+ System.getProperty(OPENNMS_HOME_PROPERTY)
+ File.separator + "etc";
}
if (System.getProperty("org.opennms.protocols.icmp.interfaceJar") != null) {
dir += File.pathSeparator + System.getProperty("org.opennms.protocols.icmp.interfaceJar");
}
if (System.getProperty("org.opennms.rrd.interfaceJar") != null) {
dir += File.pathSeparator + System.getProperty("org.opennms.rrd.interfaceJar");
}
final ClassLoader cl = Bootstrap.loadClasses(dir, false, false);
if (classToExec != null) {
final String className = classToExec;
final Class<?>[] classes = new Class[] { classToExecArgs.getClass() };
final Object[] methodArgs = new Object[] { classToExecArgs };
Class<?> c = cl.loadClass(className);
final Method method = c.getMethod(classToExecMethod, classes);
Runnable execer = new Runnable() {
public void run() {
try {
method.invoke(null, methodArgs);
} catch (final Exception e) {
e.printStackTrace();
System.exit(1);
}
}
};
Thread bootstrapper = new Thread(execer, "Main");
bootstrapper.setContextClassLoader(cl);
bootstrapper.start();
}
}
protected static void loadDefaultProperties() throws Exception {
boolean propertiesLoaded = false;
String opennmsHome = System.getProperty(OPENNMS_HOME_PROPERTY);
if (opennmsHome != null) {
propertiesLoaded = loadDefaultProperties(new File(opennmsHome));
}
if (!propertiesLoaded) {
File parent = findOpenNMSHome();
if (parent == null) {
System.err.println("Could not determine OpenNMS home "
+ "directory. Use \"-Dopennms.home=...\" "
+ "option to Java to specify a specific "
+ "OpenNMS home directory. " + "E.g.: "
+ "\"java -Dopennms.home=... -jar ...\".");
System.exit(1);
}
propertiesLoaded = loadDefaultProperties(parent);
System.setProperty(OPENNMS_HOME_PROPERTY, parent.getPath());
}
if (!propertiesLoaded) {
throw new RuntimeException("Unable to load default properties from $OPENNMS_HOME!");
}
}
}