/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat Middleware LLC, 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.jboss.bootstrap;
//$Id: ServerLoader.java 86456 2009-03-30 06:27:52Z ALRubinger $
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import org.jboss.bootstrap.spi.Server;
import org.jboss.bootstrap.spi.ServerConfig;
/**
* A helper class to load a JBoss server instance.
*
* <p>Basic usage is something like this:
* <pre>
* // setup the basic server config properties
* Properties props = new Properties(System.getProperties());
* props.put(ServerConfig.SERVER_LIBRARY_URL, "http://myserver.com/myjboss/lib/");
* // set some more properties
*
* // Obtain any addition server metadata
* Map metadata = ...;
*
* // create a new loader to do the dirty work
* ServerLoader loader = new ServerLoader(props);
*
*
* // load and initialize the server instance
* ClassLoader parent = Thread.currentThread().getContextClassLoader();
* Server server = loader.load(parent);
* server.init(props, metadata);
*
* // start up the server
* server.start();
*
* // go make some coffee, drink a beer or play GTA3
* // ...
*
* // shutdown and go to sleep
* server.shutdown();
* </pre>
* @version <tt>$Revision: 86456 $</tt>
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>
* @author Scott.Stark@jboss.org
* @author Thomas.Diesler@jboss.org
*/
public class ServerLoader
{
/**
* The default list of boot libraries. Does not include
* the JAXP or JMX impl, users of this class should add the
* proper libraries.
* TODO: use vfs to list the root directory
* http://www.jboss.org/index.html?module=bb&op=viewtopic&t=153175
*/
@Deprecated
public static final String[] DEFAULT_BOOT_LIBRARY_LIST = {
// Logging
"log4j-boot.jar",
"jboss-logging-spi.jar",
"jboss-logging-log4j.jar",
"jboss-logging-jdk.jar",
"jboss-logmanager.jar",
"jboss-logbridge.jar",
// Common jars
"jboss-common-core.jar",
"jboss-xml-binding.jar",
"jaxb-api.jar",
// Bootstrap
"jboss-bootstrap.jar",
// Microcontainer
"javassist.jar",
"jboss-reflect.jar",
"jboss-mdr.jar",
"jboss-dependency.jar",
"jboss-kernel.jar",
"jboss-metatype.jar",
"jboss-managed.jar",
// Fixme ClassLoading
"jboss-vfs.jar",
"jboss-classloading-spi.jar",
"jboss-classloader.jar",
"jboss-classloading.jar",
"jboss-classloading-vfs.jar",
// Fixme aop
"jboss-aop.jar",
"jboss-aop-mc-int.jar",
"trove.jar",
};
/** The default server type. */
public static final String DEFAULT_SERVER_TYPE = "org.jboss.bootstrap.microcontainer.ServerImpl";
/**
* Configuration properties.
*/
protected Properties props;
/**
* The URL where libraries are read from.
*/
protected URL libraryURL;
/**
* A list of extra URLs to add to the classpath when loading
* the server.
*/
protected List<URL> extraClasspath = new LinkedList<URL>();
/**
* Construct a <tt>ServerLoader</tt>.
*
* @param props Configuration properties.
*
* @throws Exception Invalid configuration
*/
public ServerLoader(final Properties props) throws Exception
{
if (props == null)
throw new IllegalArgumentException("props is null");
this.props = props;
// must have HOME_URL, or we can't continue
URL homeURL = getURL(ServerConfig.HOME_URL);
if (homeURL == null)
{
throw new Exception("Missing configuration value for: "
+ ServerConfig.HOME_URL);
}
libraryURL = getURL(ServerConfig.LIBRARY_URL);
if (libraryURL == null)
{
// need library url to make boot urls list
libraryURL = new URL(homeURL, ServerConfig.LIBRARY_URL_SUFFIX);
}
// If the home URL begins with http add the webav and httpclient jars
if( homeURL.getProtocol().startsWith("http") == true )
{
this.addLibrary("webdavlib.jar");
this.addLibrary("commons-httpclient.jar");
this.addLibrary("commons-logging.jar");
}
}
/**
* Add an extra library to the end of list of libraries
* which will be loaded from the library URL when loading
* the Server class.
*
* @param filename A filename (no directory parts)
*
* @throws MalformedURLException Could not generate URL from library URL + filename
*/
public void addLibrary(final String filename) throws MalformedURLException
{
if (filename == null)
throw new IllegalArgumentException("filename is null");
URL jarURL = new URL(libraryURL, filename);
extraClasspath.add(jarURL);
}
/**
* Add a list of comma seperated library file names.
*
* @param filenames A list of comma seperated filenames (with no directory parts)
*
* @throws MalformedURLException Could not generate URL from library URL + filename
*/
public void addLibraries(final String filenames) throws MalformedURLException
{
if (filenames == null)
throw new IllegalArgumentException("filenames is null");
StringTokenizer stok = new StringTokenizer(filenames, ",");
while (stok.hasMoreElements())
{
addLibrary(stok.nextToken().trim());
}
}
/**
* Add an extra URL to the classpath used to load the server.
*
* @param url A URL to add to the classpath.
*/
public void addURL(final URL url)
{
if (url == null)
throw new IllegalArgumentException("url is null");
extraClasspath.add(url);
}
/**
* Add the jars from the lib/endorsed dir if it exists.
* Note, the path must exist locally for this to work.
* @throws MalformedURLException Could not generate URL from library URL + filename
*/
public void addEndorsedJars() throws MalformedURLException
{
File endorsedDir = new File(libraryURL.getPath() + "/endorsed");
if (endorsedDir.exists())
{
String [] list = endorsedDir.list();
for (int i = 0; list != null && i < list.length; i++)
{
String jarname = list[i];
addLibrary("endorsed/" + jarname);
}
}
}
/**
* Get a URL from configuration or system properties.
*
* @param name the system property
* @return the url
* @throws MalformedURLException for a bad url
*/
protected URL getURL(final String name) throws MalformedURLException
{
String value = props.getProperty(name, null);
if (value != null)
{
if (!value.endsWith("/")) value += "/";
return new URL(value);
}
return null;
}
/**
* Returns an array of URLs which will be used to load the
* core system and construct a new Server object instance.
* @return the urls
* @throws MalformedURLException for a bad url
*/
protected URL[] getBootClasspath() throws MalformedURLException, URISyntaxException
{
List<URL> list = new LinkedList<URL>();
// prepend users classpath to allow for overrides
list.addAll(extraClasspath);
String value = props.getProperty(ServerConfig.BOOT_LIBRARY_LIST);
if( value != null )
{
StringTokenizer stok = new StringTokenizer(value, ",");
while (stok.hasMoreElements())
{
URL url = new URL(libraryURL, stok.nextToken().trim());
list.add(url);
}
}
else
{
for (String jar : DEFAULT_BOOT_LIBRARY_LIST)
{
URL url = new URL(libraryURL, jar);
list.add(url);
}
}
return list.toArray(new URL[list.size()]);
}
/**
* Load a {@link Server} instance.
*
* @parent The parent of any class loader created during boot.
* @return An uninitialized (and unstarted) Server instance.
*
* @param parent the parent classloader
* @throws Exception Failed to load or create Server instance.
*/
public Server load(final ClassLoader parent) throws Exception
{
Server server;
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
try
{
// get the boot lib list
URL[] urls = getBootClasspath();
URLClassLoader classLoader = new NoAnnotationURLClassLoader(urls, parent);
Thread.currentThread().setContextClassLoader(classLoader);
/*
// Log the boot URLs (uncomment to test)
StringBuffer sb = new StringBuffer("Boot URLs:\n");
for (URL url : urls)
{
sb.append(url.toExternalForm());
sb.append("\n");
}
System.out.println(sb);
*/
// construct a new Server instance
String typename = props.getProperty(ServerConfig.SERVER_TYPE, DEFAULT_SERVER_TYPE);
server = createServer(typename, classLoader);
}
finally
{
Thread.currentThread().setContextClassLoader(oldCL);
}
// thats all folks, have fun
return server;
}
/**
* Construct a new instance of Server, loading all required classes from
* the given ClassLoader.
* @param typename - the fqcn of the Server implementation
* @param loader - the ClassLoader to load typename with
* @return the server
* @throws Exception for any error
*/
protected Server createServer(final String typename, final ClassLoader loader)
throws Exception
{
// load the class first
Class<?> type = loader.loadClass(typename);
// and then create a new instance
Server server = (Server) type.newInstance();
return server;
}
}