/******************************************************************************* * Copyright (c) 2001, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.j2ee.commonarchivecore.internal.util; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.security.ProtectionDomain; import java.util.HashSet; import java.util.Set; import org.eclipse.jst.j2ee.commonarchivecore.internal.Archive; import org.eclipse.jst.j2ee.commonarchivecore.internal.CommonArchiveResourceHandler; import org.eclipse.jst.j2ee.commonarchivecore.internal.EARFile; import org.eclipse.jst.j2ee.commonarchivecore.internal.File; import org.eclipse.jst.j2ee.commonarchivecore.internal.exception.ArchiveRuntimeException; import org.eclipse.jst.j2ee.core.internal.plugin.J2EECorePlugin; /** * Class loader which loads a given set of classes stored in byte arrays. (Assumption: System * classes and those in the set are the only classes needed to resolve each one) */ public class ArchiveFileDynamicClassLoader extends ClassLoader { protected Archive archive = null; protected ClassLoader extraClassLoader; protected boolean inEARFile; private static final String URL_PROTOCOL = "archive"; //$NON-NLS-1$ private ArchiveURLStreamHandler handler; protected ProtectionDomain protectionDomain; /** * <p>This constructor accepts a protection domain, which is used * by <code>findClass</code>.</p> * * @see ArchiveFileDynamicClassLoader#findClass(String) */ public ArchiveFileDynamicClassLoader(Archive anArchive, ClassLoader parentCl, ClassLoader extraCl, ProtectionDomain pDomain) { super(parentCl); setArchive(anArchive); setExtraClassLoader(extraCl); inEARFile = anArchive.getContainer() != null && anArchive.getContainer().isEARFile(); handler = new ArchiveURLStreamHandler(); protectionDomain = pDomain; } public ArchiveFileDynamicClassLoader(Archive anArchive, ClassLoader parentCl, ClassLoader extraCl) { this(anArchive, parentCl, extraCl, null); } /** * <p>Loads a specified class. Called only after the parent class loader has had * its chance to load the class, as per the Java2 delegation model.</p> * * <p>When non-null, the receiver's protection * domain is passed in to the call to <code>defineClass</code>.</p> * * @see ClassLoader#defineClass(String, byte[], int) * @see ClassLoader#defineClass(String, byte[], int, ProtectionDomain) */ @Override protected Class findClass(String name) throws ClassNotFoundException { Class result; // Load class bytes from current set of class byte[]'s byte[] bytes = getClassBytesFor(name); if (bytes != null) { if ( protectionDomain == null ) { result = defineClass(name, bytes, 0, bytes.length); } else { result = defineClass(name, bytes, 0, bytes.length, protectionDomain); } if (result == null) { throw new ClassNotFoundException(name); } // endif } else { throw new ClassNotFoundException(name); } // endif return result; } /** * Insert the method's description here. Creation date: (12/17/00 9:59:57 PM) * * @return com.ibm.etools.commonarchive.Archive */ public Archive getArchive() { return archive; } private byte[] getData(File file) { if (null != file) { try { return ArchiveUtil.inputStreamToBytes(file.getInputStream()); } catch (FileNotFoundException e) { return null; } catch (IOException e) { throw new ArchiveRuntimeException(CommonArchiveResourceHandler.getString(CommonArchiveResourceHandler.io_ex_loading_EXC_, (new Object[]{file.getName()})), e); // = "An IO exception occurred loading " } } } return null; } protected byte[] getClassBytesFor(String className) { if (className == null) return null; // Change the class name to a jar entry name String jarEntryName = ArchiveUtil.classNameToUri(className); return getData(getFile(jarEntryName)); } protected EARFile getEARFile() { return (EARFile) getArchive().getContainer(); } /** * Insert the method's description here. Creation date: (11/21/00 6:58:05 PM) * * @return java.lang.ClassLoader */ public java.lang.ClassLoader getExtraClassLoader() { return extraClassLoader; } /** * Used for dynamic class loading in dependent jars in ears; the set is used to terminate a * cycle if one exists; the cycle is invalid, but you never know what people might try... */ protected synchronized Class loadClass(String name, Set visitedArchives) throws ClassNotFoundException { if (visitedArchives.contains(getArchive())) throw new ClassNotFoundException(name); visitedArchives.add(getArchive()); try { return super.loadClass(name, false); } catch (ClassNotFoundException ex) { return loadClassInDependentJarInEAR(name, visitedArchives); } } @Override protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { try { return super.loadClass(name, resolve); } catch (ClassNotFoundException ex) { Class c = loadClassInDependentJar(name); if (c != null && resolve) resolveClass(c); return c; } } protected Class loadClassInDependentJar(String name) throws ClassNotFoundException { if (inEARFile) { return loadClassInDependentJarInEAR(name); } else if (getExtraClassLoader() != null) { return getExtraClassLoader().loadClass(name); } throw new ClassNotFoundException(name); } protected Class loadClassInDependentJarInEAR(String name, Set visitedArchives) throws ClassNotFoundException { Object obj = getResourceInDependentJarInEAR(name, visitedArchives, CLASS_TYPE); if (obj == null) { throw new ClassNotFoundException(name); } return (Class) obj; } protected Class loadClassInDependentJarInEAR(String name) throws ClassNotFoundException { Object obj = getResourceInDependentJarInEAR(name, CLASS_TYPE); if (obj == null) { throw new ClassNotFoundException(name); } return (Class) obj; } protected File getFileFromDependentJar(String name) { Object obj = getResourceInDependentJarInEAR(name, FILE_TYPE); if (obj != null) { return (File) obj; } return null; } protected static final int CLASS_TYPE = 0; protected static final int FILE_TYPE = 1; protected Object getResourceInDependentJarInEAR(String name, int type) { Set visitedArchives = new HashSet(5); visitedArchives.add(getArchive()); return getResourceInDependentJarInEAR(name, visitedArchives, type); } protected Object getResourceInDependentJarInEAR(String name, Set visitedArchives, int type) { String[] classpath = archive.getManifest().getClassPathTokenized(); for (int i = 0; i < classpath.length; i++) { try { String uri = ArchiveUtil.deriveEARRelativeURI(classpath[i], archive); if (uri == null) continue; File jarFile = getEARFile().getFile(uri); if (jarFile.isArchive()) { Archive dep = (Archive) jarFile; switch (type) { case CLASS_TYPE : try { return ((ArchiveFileDynamicClassLoader) dep.getArchiveClassLoader()).loadClass(name, visitedArchives); } catch (ClassNotFoundException noDice) { continue; } case FILE_TYPE : try { return dep.getFile(name); } catch (FileNotFoundException noDice) { continue; } } } } catch (java.io.FileNotFoundException depJarNotInEAR) { } } return null; } /** * Insert the method's description here. Creation date: (12/17/00 9:59:57 PM) * * @param newArchive * com.ibm.etools.commonarchive.Archive */ public void setArchive(Archive newArchive) { archive = newArchive; } /** * Insert the method's description here. Creation date: (11/21/00 6:58:05 PM) * * @param newExtraClassLoader * java.lang.ClassLoader */ public void setExtraClassLoader(java.lang.ClassLoader newExtraClassLoader) { extraClassLoader = newExtraClassLoader; } @Override public InputStream getResourceAsStream(String name) { try { File file = getFile(name); if (null != file) { return file.getInputStream(); } } catch (IOException e) { throw new ArchiveRuntimeException(CommonArchiveResourceHandler.getString(CommonArchiveResourceHandler.io_ex_loading_EXC_, (new Object[]{name})), e); // = "An IO exception occurred loading " } return null; } protected File getFileFromArchive(String name) { try { return getArchive().getFile(name); } catch (FileNotFoundException e) { } return null; } protected File getFile(String name) { File file = getFileFromArchive(name); if (file == null) { file = getFileFromDependentJar(name); } return file; } @Override protected URL findResource(String name) { if (getFile(name) != null) { try { return new URL(null, URL_PROTOCOL + "://" + name, handler); //$NON-NLS-1$ } catch (MalformedURLException e) { J2EECorePlugin.logError(e); throw new RuntimeException(e); } } return null; } private class ArchiveURLStreamHandler extends URLStreamHandler { public ArchiveURLStreamHandler() { } @Override protected URLConnection openConnection(URL url) throws IOException { return new ArchiveURLConnection(url); } } private class ArchiveURLConnection extends URLConnection { private String resourceName; protected ArchiveURLConnection(URL url) { super(url); resourceName = url.toString().substring(URL_PROTOCOL.length() + 3); } @Override public void connect() throws IOException { } @Override public InputStream getInputStream() throws IOException { return getResourceAsStream(resourceName); } } }