/*
* Copyright 2003,2004 Colin Crist
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package hermes.impl;
import hermes.browser.HermesBrowser;
import hermes.config.ClasspathConfig;
import hermes.config.PropertyConfig;
import hermes.config.PropertySetConfig;
import hermes.util.TextUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.jms.ConnectionFactory;
import javax.swing.ProgressMonitor;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
/**
* Helper for mucking about with ClassLoaders
*
* @author colincrist@hermesjms.com
* @version $Id: LoaderSupport.java,v 1.29 2006/07/13 07:35:31 colincrist Exp $
*/
public class LoaderSupport
{
private static final Logger log = Logger.getLogger(LoaderSupport.class);
public static class DebugClassLoader extends URLClassLoader
{
/**
* @param arg0
*/
public DebugClassLoader(URL[] arg0)
{
super(arg0);
}
/**
* @param arg0
* @param arg1
*/
public DebugClassLoader(URL[] arg0, ClassLoader arg1)
{
super(arg0, arg1);
}
/**
* @param arg0
* @param arg1
* @param arg2
*/
public DebugClassLoader(URL[] arg0, ClassLoader arg1, URLStreamHandlerFactory arg2)
{
super(arg0, arg1, arg2);
}
/*
* (non-Javadoc)
*
* @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
*/
protected synchronized Class loadClass(String arg0, boolean arg1) throws ClassNotFoundException
{
if (arg0.startsWith("hermes.ext"))
{
log.debug("loadClass(" + arg0 + ", " + arg1 + ") from " + toString());
}
return super.loadClass(arg0, arg1);
}
public String toString()
{
StringBuffer rval = new StringBuffer();
rval.append("DebugClassLoader: ");
for (int i = 0; i < getURLs().length; i++)
{
URL url = getURLs()[i];
rval.append(url.toString());
if (i != getURLs().length - 1)
{
rval.append(", ");
}
}
if (getParent() instanceof DebugClassLoader)
{
rval.append(", parent=" + getParent().toString());
}
return rval.toString();
}
protected Class findClass(String name) throws ClassNotFoundException
{
return super.findClass(name);
}
}
/**
* Return ClassLoader given the list of ClasspathConfig instances. The
* resulting loader can then be used to instantiate providers from those
* libraries.
*/
static ClassLoader createClassLoader(List loaderConfigs, URL[] extraUrls, ClassLoader classLoader) throws IOException
{
int index = 0;
int size = loaderConfigs.size();
if (extraUrls != null)
{
size += extraUrls.length;
}
URL[] urls = new URL[size];
StringBuffer debug = new StringBuffer("URLClassLoader: ");
for (Iterator iter = loaderConfigs.iterator(); iter.hasNext();)
{
ClasspathConfig lConfig = (ClasspathConfig) iter.next();
URL url = null;
if (lConfig.getJar().startsWith("http"))
{
url = new URL(TextUtils.replaceClasspathVariables(lConfig.getJar()));
}
else
{
url = new File(TextUtils.replaceClasspathVariables(lConfig.getJar())).toURL();
}
urls[index++] = url;
debug.append(url.toString());
if (iter.hasNext())
{
debug.append(", ");
}
}
if (extraUrls != null)
{
for (int i = 0; i < extraUrls.length; i++)
{
urls[index++] = extraUrls[i];
debug.append(", " + extraUrls[i].toString());
}
}
log.debug(debug.toString());
return new DebugClassLoader(urls, classLoader);
}
static ClassLoader createClassLoader(List loaderConfigs, ClassLoader classLoader) throws IOException
{
return createClassLoader(loaderConfigs, null, classLoader);
}
/**
* Return ClassLoader given the list of ClasspathConfig instances. The
* resulting loader can then be used to instantiate providers from those
* libraries.
*/
static List lookForFactories(final List loaderConfigs, final ClassLoader baseLoader) throws IOException
{
final List rval = new ArrayList();
for (Iterator iter = loaderConfigs.iterator(); iter.hasNext();)
{
final ClasspathConfig lConfig = (ClasspathConfig) iter.next();
if (lConfig.getFactories() != null)
{
log.debug("using cached " + lConfig.getFactories());
for (StringTokenizer tokens = new StringTokenizer(lConfig.getFactories(), ","); tokens.hasMoreTokens();)
{
rval.add(tokens.nextToken());
}
}
else if (lConfig.isNoFactories())
{
log.debug("previously scanned " + lConfig.getJar());
}
else
{
Runnable r = new Runnable()
{
public void run()
{
final List localFactories = new ArrayList();
boolean foundFactory = false;
StringBuffer factoriesAsString = null;
try
{
log.debug("searching " + lConfig.getJar());
ClassLoader l = createClassLoader(loaderConfigs, baseLoader);
JarFile jarFile = new JarFile(lConfig.getJar());
ProgressMonitor monitor = null;
int entryNumber = 0;
if (HermesBrowser.getBrowser() != null)
{
monitor = new ProgressMonitor(HermesBrowser.getBrowser(), "Looking for factories in " + lConfig.getJar(), "Scanning...", 0, jarFile
.size());
monitor.setMillisToDecideToPopup(0);
monitor.setMillisToPopup(0);
monitor.setProgress(0);
}
for (Enumeration iter = jarFile.entries(); iter.hasMoreElements();)
{
ZipEntry entry = (ZipEntry) iter.nextElement();
entryNumber++;
if (monitor != null)
{
monitor.setProgress(entryNumber);
monitor.setNote("Checking entry " + entryNumber + " of " + jarFile.size());
}
if (entry.getName().endsWith(".class"))
{
String s = entry.getName().substring(0, entry.getName().indexOf(".class"));
s = s.replaceAll("/", ".");
try
{
if (s.startsWith("hermes.browser") || s.startsWith("hermes.impl") || s.startsWith("javax.jms"))
{
// NOP
}
else
{
Class clazz = l.loadClass(s);
if (!clazz.isInterface())
{
if (implementsOrExtends(clazz, ConnectionFactory.class))
{
foundFactory = true;
localFactories.add(s);
if (factoriesAsString == null)
{
factoriesAsString = new StringBuffer();
factoriesAsString.append(clazz.getName());
}
else
{
factoriesAsString.append(",").append(clazz.getName());
}
log.debug("found " + clazz.getName());
}
}
/**
* TODO: remove Class clazz = l.loadClass(s);
* Class[] interfaces = clazz.getInterfaces();
* for (int i = 0; i < interfaces.length; i++) {
* if
* (interfaces[i].equals(TopicConnectionFactory.class) ||
* interfaces[i].equals(QueueConnectionFactory.class) ||
* interfaces[i].equals(ConnectionFactory.class)) {
* foundFactory = true; localFactories.add(s);
* if (factoriesAsString == null) {
* factoriesAsString = new
* StringBuffer(clazz.getName()); } else {
* factoriesAsString.append(",").append(clazz.getName()); }
* log.debug("found " + clazz.getName()); } }
*/
}
}
catch (Throwable t)
{
// NOP
}
}
}
}
catch (IOException e)
{
log.error("unable to access jar/zip " + lConfig.getJar() + ": " + e.getMessage(), e);
}
if (!foundFactory)
{
lConfig.setNoFactories(true);
}
else
{
lConfig.setFactories(factoriesAsString.toString());
rval.addAll(localFactories);
}
}
};
r.run();
}
}
return rval;
}
/**
* Indicates if a class or interface implements or extends the specified
* interfaces or classes. If the specified <CODE>Class</CODE> is a class,
* this method will recursively test if this class, its superclass or one of
* the implemented interfaces of this class implements the specified
* interface.<BR>
* If the specified <CODE>Class</CODE> is an interface, this method will
* recursively test if this interface or one of the implemented interfaces of
* this interface implements the specified interface.<BR>
*
* @param clazz
* the class or interface in question
* @param testInterface
* the class or interface to test against
* @return <CODE>true</CODE> if the specified interfaces is implemented by
* this class or one of its super-classes or interfaces
*/
public static boolean implementsOrExtends(Class clazz, Class testInterface)
{
Class[] implementedInterfaces = clazz.getInterfaces();
// test interface
if (clazz.equals(testInterface))
{
return true; // possibly the end of the recursion
}
for (int i = 0; i < implementedInterfaces.length; i++)
{
if (implementsOrExtends(implementedInterfaces[i], testInterface))
{
return true; // recursion
}
}
// maybe the superclass implements this interface ?
Class superClass = clazz.getSuperclass();
if (superClass != null && implementsOrExtends(superClass, testInterface))
{
return true; // recursion
}
return false;
}
public static void populateBean(Object bean, PropertySetConfig propertySet) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, IOException
{
if (propertySet != null)
{
Set appliedProperties = new HashSet();
for (Iterator iter = propertySet.getProperty().iterator(); iter.hasNext();)
{
PropertyConfig propertyConfig = (PropertyConfig) iter.next();
if (appliedProperties.contains(propertyConfig.getName()))
{
iter.remove();
}
else
{
try
{
BeanUtils.setProperty(bean, propertyConfig.getName(), TextUtils.replaceClasspathVariables(propertyConfig.getValue()));
appliedProperties.add(propertyConfig.getName());
log.debug("set " + bean.getClass().getName() + " " + propertyConfig.getName() + "=" + TextUtils.replaceClasspathVariables(propertyConfig.getValue())) ;
}
catch (InvocationTargetException t)
{
log.error("unable to set property name=" + propertyConfig.getName() + " value=" + propertyConfig.getValue() + " on object of class "
+ bean.getClass().getName() + ": " + t.getCause().getMessage(), t.getCause());
}
}
}
}
}
}