/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.testing.oxm.classloader; import java.io.*; import java.net.URISyntaxException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.util.*; import java.util.zip.*; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.internal.security.PrivilegedGetClassLoaderForClass; /** * This class is an implementation of the <code>ClassLoader</code> abstract class. * Its purpose is to allow the ability to load a class from * a particular JAR file without regard to the system class path. * * @author Big Country * @since TOPLink/Java 3.0 */ public class JARClassLoader extends ClassLoader { /** Map each package name to the appropriate JAR file. */ private Hashtable overridePackageNames; /** * Default constructor - initialize the new instance. */ protected JARClassLoader() { super(); this.initialize(); } /** * Construct a file loader for the JAR files. */ public JARClassLoader (String[] jarFileNames) { this(); this.initialize(jarFileNames); } /** * Construct a file loader for the JAR file. */ public JARClassLoader (String jarFileName) { this(); this.initialize(jarFileName); } /** * Build and return a ZIP file for the specified JAR file name. */ protected ZipFile buildJARFile(String jarFileName) { try { return new ZipFile(new File(Thread.currentThread().getContextClassLoader().getResource(jarFileName).toURI())); } catch (IOException e) { throw new RuntimeException(e); } catch (URISyntaxException e) { throw new RuntimeException(e); } } /** * Return the package name for the specified class. */ protected String buildPackageName(String className) { return className.substring(0, className.lastIndexOf(".")); } /** * Return the class for the specified name, leaving it "unresolved". */ protected Class customLoadUnresolvedClass(String className) throws ClassNotFoundException { String jarFileName = (String) overridePackageNames.get(this.buildPackageName(className)); ZipFile jarFile = this.buildJARFile(jarFileName); String url = className.replace('.', '/').concat(".class"); ZipEntry jarEntry = jarFile.getEntry(url); if(jarEntry == null){ return this.getParent().loadClass(className); } byte[] data; try { data = this.loadData(jarFile, jarEntry); jarFile.close(); } catch (IOException e) { Object[] args = {jarFileName, url}; throw new ClassNotFoundException(); } return this.defineClass(className, data, 0, data.length); } /** * Extract the package names from the specified JAR file * and store them in the hashtable. */ protected void extractPackageNames(String jarFileName) { Enumeration stream = this.buildJARFile(jarFileName).entries(); while (stream.hasMoreElements()) { String entryName = ((ZipEntry) stream.nextElement()).getName(); int endIndex = entryName.lastIndexOf("/"); // skip over entries for files in the root directory and directories if ((endIndex != -1) && (endIndex != (entryName.length() - 1))) { String packageName = (entryName.substring(0, endIndex)).replace('/', '.'); // skip over JAR META-INF directory if (! packageName.equals("META-INF")) { overridePackageNames.put(packageName, jarFileName); } } } } /** * Initialize the newly-created instance. */ protected void initialize() { this.overridePackageNames = new Hashtable(); } /** * Initialize */ protected void initialize(String[] jarFileNames) { if (jarFileNames == null) { throw new IllegalArgumentException(); } // go backwards to maintain search order (earlier packages will overlay later ones) for (int i = jarFileNames.length - 1; i >= 0; i--) { this.extractPackageNames(jarFileNames[i]); } } /** * Initialize */ protected void initialize(String jarFileName) { if (jarFileName == null) { throw new IllegalArgumentException(); } this.initialize(new String[] {jarFileName}); } /** * Return the class for the specified name, resolving it if necessary. */ protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException { Class c = this.loadUnresolvedClass(className); if (resolve) { this.resolveClass(c); } return c; } /** * Return a byte array holding the contents of the specified file. */ protected byte[] loadData(ZipFile jarFile, ZipEntry jarEntry) throws IOException { int size = (int) jarEntry.getSize(); byte[] buffer = new byte[size]; DataInputStream stream = new DataInputStream(jarFile.getInputStream(jarEntry)); stream.readFully(buffer); stream.close(); return buffer; } /** * Return the class for the specified name. */ protected Class loadUnresolvedClass(String className) throws ClassNotFoundException { // check whether we already loaded the class Class c = this.findLoadedClass(className); if (c != null) { return c; } // check whether we should custom load the class if (this.shouldCustomLoad(className)) { return this.customLoadUnresolvedClass(className); } else { ClassLoader cl = null; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { try{ cl = AccessController.doPrivileged(new PrivilegedGetClassLoaderForClass(this.getClass())); }catch (PrivilegedActionException ex){ throw (RuntimeException)ex.getCause(); } }else{ cl = PrivilegedAccessHelper.getClassLoaderForClass(this.getClass()); } if (cl == null) { // this should only occur under jdk1.1.x return this.findSystemClass(className); } else { return cl.loadClass(className); } } } /** * Return whether the specified class should be custom loaded. * If it is a member of one of the override packages, it should be * custom loaded. */ protected boolean shouldCustomLoad(String className) { return overridePackageNames.containsKey(this.buildPackageName(className)); } }