/*
* Copyright 2002-2004 Greg Hinkle
*
* 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 org.mc4j.ems.connection.support.classloader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mc4j.ems.connection.EmsConnectException;
import org.mc4j.ems.connection.EmsException;
import org.mc4j.ems.connection.settings.ConnectionSettings;
import org.mc4j.ems.connection.support.metadata.JSR160ConnectionTypeDescriptor;
import org.mc4j.ems.connection.support.metadata.WeblogicConnectionTypeDescriptor;
import org.mc4j.ems.connection.support.metadata.WebsphereConnectionTypeDescriptor;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
/**
* @author Greg Hinkle (ghinkle@users.sourceforge.net), Apr 5, 2005
* @version $Revision: 1.3 $($Author: ghinkl $ / $Date: 2006/05/22 02:38:52 $)
*/
public class ClassLoaderFactory {
private static ClassLoaderFactory INSTANCE;
private static Log log = LogFactory.getLog(ClassLoaderFactory.class);
private static Map<String, File> jarCache = new HashMap<String, File>();
static {
String className = System.getProperty("org.mc4j.ems.classloaderfactory");
if (className != null) {
try {
INSTANCE = ((Class<ClassLoaderFactory>) Class.forName(className)).newInstance();
} catch (Exception e) {
throw new EmsException("Unable to load custom classloader factory " + className, e);
}
}
if (INSTANCE == null) {
INSTANCE = new ClassLoaderFactory();
}
}
/**
* Retrieves the configured classloader factory for EMS. This can be customized by
* setting the system property "org.mc4j.ems.classloaderfactory".
*
* @return the Classloader Factory used to build the connection classloader
*/
public static ClassLoaderFactory getInstance() {
return INSTANCE;
}
/**
* TODO GH: Implement a special classloader that can load classes from
* within a jar inside another jar or perhaps just ship the impl jar separately...
*/
protected URL storeImplToTemp(String archiveResource) {
try {
if (jarCache.containsKey(archiveResource)) {
return jarCache.get(archiveResource).toURI().toURL();
}
InputStream is = ClassLoaderFactory.class.getClassLoader().getResourceAsStream(archiveResource);
if (is == null) {
throw new EmsException("Unable to find resource to store [" + archiveResource + "]");
}
// String tmpPath = System.getProperty("java.io.tmpdir");
String jarName = new File(archiveResource).getName();
jarName = jarName.substring(0, jarName.length() - 4);
File tmpFile = File.createTempFile(jarName, ".jar");
tmpFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tmpFile);
byte[] buffer = new byte[4096];
int size = is.read(buffer);
while (size != -1) {
fos.write(buffer, 0, size);
size = is.read(buffer);
}
fos.close();
is.close();
jarCache.put(archiveResource, tmpFile);
return tmpFile.toURI().toURL();
} catch (FileNotFoundException e) {
throw new EmsException("Unable to make temporary file store",e);
} catch (IOException e) {
throw new EmsException("Unable to make temporary file store",e);
}
}
public ClassLoader buildClassLoader(ConnectionSettings settings) {
// TODO GH: Implement configurable system to point at jar instead of creating temporary version
List<URL> entries = new ArrayList<URL>();
if (settings.getClassPathEntries() != null) {
for (File file : settings.getClassPathEntries()) {
try {
entries.add(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new EmsConnectException("Unable to read class path library url", e);
}
}
}
// Now load in the implementation jar
// URL implURL = new URL(null, "deepjar://org-mc4j-ems-impl.jar", new Handler());
URL implURL = storeImplToTemp("org-mc4j-ems-impl.jar");
entries.add(implURL);
// Add internal support jars for JSR160 on < jdk5
if ((settings.getConnectionType() instanceof JSR160ConnectionTypeDescriptor) &&
settings.getConnectionType().getConnectionClasspathEntries() == null &&
Double.parseDouble(System.getProperty("java.version").substring(0, 3)) < 1.5) {
entries.add(storeImplToTemp("lib/jsr160-includes/mx4j.jar"));
entries.add(storeImplToTemp("lib/jsr160-includes/mx4j-remote.jar"));
}
// TODO: Check if file exists, log warning if not
URL[] entryArray = entries.toArray(new URL[entries.size()]);
// WARNING: Relatively disgusting hack. hiding classes is not a good thing
URLClassLoader loader = null;
if (settings.getConnectionType().isUseChildFirstClassLoader()) {
loader = new ChildFirstClassloader(entryArray, ClassLoaderFactory.class.getClassLoader());
} else {
// TODO was NestedJarClassLoader
//loader = new ChildFirstClassloader(entryArray, ClassLoaderFactory.class.getClassLoader());
loader = new URLClassLoader(entryArray, ClassLoaderFactory.class.getClassLoader());
//loader = new NestedJarClassLoader(entryArray, ClassLoaderFactory.class.getClassLoader());
}
if (log.isDebugEnabled()) {
StringBuffer buf = new StringBuffer("Classloader built with: \n");
for (URL url : entries) {
buf.append("\t").append(url).append("\n");
}
log.debug(buf.toString());
}
return loader;
}
}