325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.istack.internal.tools;
325N/A
325N/Aimport java.io.InputStream;
325N/Aimport java.io.ByteArrayOutputStream;
325N/Aimport java.io.Closeable;
325N/Aimport java.io.File;
325N/Aimport java.io.IOException;
325N/Aimport java.net.JarURLConnection;
325N/Aimport java.net.URISyntaxException;
325N/Aimport java.net.URL;
325N/Aimport java.net.MalformedURLException;
325N/Aimport java.net.URLConnection;
325N/Aimport java.util.Collections;
325N/Aimport java.util.Enumeration;
325N/Aimport java.util.HashSet;
325N/Aimport java.util.Set;
325N/Aimport java.util.jar.JarFile;
325N/Aimport java.util.logging.Level;
325N/Aimport java.util.logging.Logger;
325N/A
325N/A/**
325N/A * Load classes/resources from a side folder, so that
325N/A * classes of the same package can live in a single jar file.
325N/A *
325N/A * <p>
325N/A * For example, with the following jar file:
325N/A * <pre>
325N/A * /
325N/A * +- foo
325N/A * +- X.class
325N/A * +- bar
325N/A * +- X.class
325N/A * </pre>
325N/A * <p>
325N/A * {@link ParallelWorldClassLoader}("foo/") would load <tt>X.class<tt> from
325N/A * <tt>/foo/X.class</tt> (note that X is defined in the root package, not
325N/A * <tt>foo.X</tt>.
325N/A *
325N/A * <p>
325N/A * This can be combined with {@link MaskingClassLoader} to mask classes which are loaded by the parent
325N/A * class loader so that the child class loader
325N/A * classes living in different folders are loaded
325N/A * before the parent class loader loads classes living the jar file publicly
325N/A * visible
325N/A * For example, with the following jar file:
325N/A * <pre>
325N/A * /
325N/A * +- foo
325N/A * +- X.class
325N/A * +- bar
325N/A * +-foo
325N/A * +- X.class
325N/A * </pre>
325N/A * <p>
325N/A * {@link ParallelWorldClassLoader}(MaskingClassLoader.class.getClassLoader()) would load <tt>foo.X.class<tt> from
325N/A * <tt>/bar/foo.X.class</tt> not the <tt>foo.X.class<tt> in the publicly visible place in the jar file, thus
325N/A * masking the parent classLoader from loading the class from <tt>foo.X.class<tt>
325N/A * (note that X is defined in the package foo, not
325N/A * <tt>bar.foo.X</tt>.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic class ParallelWorldClassLoader extends ClassLoader implements Closeable {
325N/A
325N/A /**
325N/A * Strings like "prefix/", "abc/", or "" to indicate
325N/A * classes should be loaded normally.
325N/A */
325N/A private final String prefix;
325N/A private final Set<JarFile> jars;
325N/A
325N/A public ParallelWorldClassLoader(ClassLoader parent,String prefix) {
325N/A super(parent);
325N/A this.prefix = prefix;
325N/A jars = Collections.synchronizedSet(new HashSet<JarFile>());
325N/A }
325N/A
325N/A protected Class findClass(String name) throws ClassNotFoundException {
325N/A StringBuffer sb = new StringBuffer(name.length()+prefix.length()+6);
325N/A sb.append(prefix).append(name.replace('.','/')).append(".class");
325N/A
325N/A URL u = getParent().getResource(sb.toString());
325N/A if (u == null) {
325N/A throw new ClassNotFoundException(name);
325N/A }
325N/A
325N/A InputStream is = null;
325N/A URLConnection con = null;
325N/A
325N/A try {
325N/A con = u.openConnection();
325N/A is = con.getInputStream();
325N/A } catch (IOException ioe) {
325N/A throw new ClassNotFoundException(name);
325N/A }
325N/A
325N/A if (is==null)
325N/A throw new ClassNotFoundException(name);
325N/A
325N/A try {
325N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
325N/A byte[] buf = new byte[1024];
325N/A int len;
325N/A while((len=is.read(buf))>=0)
325N/A baos.write(buf,0,len);
325N/A
325N/A buf = baos.toByteArray();
325N/A int packIndex = name.lastIndexOf('.');
325N/A if (packIndex != -1) {
325N/A String pkgname = name.substring(0, packIndex);
325N/A // Check if package already loaded.
325N/A Package pkg = getPackage(pkgname);
325N/A if (pkg == null) {
325N/A definePackage(pkgname, null, null, null, null, null, null, null);
325N/A }
325N/A }
325N/A return defineClass(name,buf,0,buf.length);
325N/A } catch (IOException e) {
325N/A throw new ClassNotFoundException(name,e);
325N/A } finally {
325N/A try {
325N/A if (con != null && con instanceof JarURLConnection) {
325N/A jars.add(((JarURLConnection) con).getJarFile());
325N/A }
325N/A } catch (IOException ioe) {
325N/A //ignore
325N/A }
325N/A if (is != null) {
325N/A try {
325N/A is.close();
325N/A } catch (IOException ioe) {
325N/A //ignore
325N/A }
325N/A }
325N/A }
325N/A }
325N/A
325N/A @Override
325N/A protected URL findResource(String name) {
325N/A URL u = getParent().getResource(prefix + name);
325N/A if (u != null) {
325N/A try {
325N/A jars.add(new JarFile(new File(toJarUrl(u).toURI())));
325N/A } catch (URISyntaxException ex) {
325N/A Logger.getLogger(ParallelWorldClassLoader.class.getName()).log(Level.WARNING, null, ex);
325N/A } catch (IOException ex) {
325N/A Logger.getLogger(ParallelWorldClassLoader.class.getName()).log(Level.WARNING, null, ex);
325N/A } catch (ClassNotFoundException ex) {
325N/A //ignore - not a jar
325N/A }
325N/A }
325N/A return u;
325N/A }
325N/A
325N/A @Override
325N/A protected Enumeration<URL> findResources(String name) throws IOException {
325N/A Enumeration<URL> en = getParent().getResources(prefix + name);
325N/A while (en.hasMoreElements()) {
325N/A try {
325N/A jars.add(new JarFile(new File(toJarUrl(en.nextElement()).toURI())));
325N/A } catch (URISyntaxException ex) {
325N/A //should not happen
325N/A Logger.getLogger(ParallelWorldClassLoader.class.getName()).log(Level.WARNING, null, ex);
325N/A } catch (IOException ex) {
325N/A Logger.getLogger(ParallelWorldClassLoader.class.getName()).log(Level.WARNING, null, ex);
325N/A } catch (ClassNotFoundException ex) {
325N/A //ignore - not a jar
325N/A }
325N/A }
325N/A return en;
325N/A }
325N/A
325N/A public synchronized void close() throws IOException {
325N/A for (JarFile jar : jars) {
325N/A jar.close();
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Given the URL inside jar, returns the URL to the jar itself.
325N/A */
325N/A public static URL toJarUrl(URL res) throws ClassNotFoundException, MalformedURLException {
325N/A String url = res.toExternalForm();
325N/A if(!url.startsWith("jar:"))
325N/A throw new ClassNotFoundException("Loaded outside a jar "+url);
325N/A url = url.substring(4); // cut off jar:
325N/A url = url.substring(0,url.lastIndexOf('!')); // cut off everything after '!'
325N/A return new URL(url);
325N/A }
325N/A}