1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.xjc.api.util;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.File;
  32 import java.net.URL;
  33 import java.net.URLClassLoader;
  34 import java.net.MalformedURLException;
  35 
  36 import com.sun.istack.internal.Nullable;
  37 
  38 /**
  39  * {@link ClassLoader} that loads Annotation Processing and specified classes
  40  * both into the same classloader, so that they can reference each other.
  41  *
  42  * @author Bhakti Mehta
  43  * @since 2.0 beta
  44  */
  45 public final class ApClassLoader extends URLClassLoader {
  46     /**
  47      * List of package prefixes we want to mask the
  48      * parent classLoader from loading
  49      */
  50     private final String[] packagePrefixes;
  51 
  52     /**
  53      *
  54      * @param packagePrefixes
  55      *      The package prefixes that are forced to resolve within this class loader.
  56      * @param parent
  57      *      The parent class loader to delegate to. Null to indicate bootstrap classloader.
  58      */
  59     public ApClassLoader(@Nullable ClassLoader parent, String[] packagePrefixes) throws ToolsJarNotFoundException {
  60         super(getToolsJar(parent),parent);
  61         if(getURLs().length==0)
  62             // if tools.jar was found in our classloader, no need to create
  63             // a parallel classes
  64             this.packagePrefixes = new String[0];
  65         else
  66             this.packagePrefixes = packagePrefixes;
  67     }
  68 
  69     public Class loadClass(String className) throws ClassNotFoundException {
  70         for( String prefix : packagePrefixes ) {
  71             if (className.startsWith(prefix) ) {
  72                 // we need to load those classes in this class loader
  73                 // without delegation.
  74                 return findClass(className);
  75             }
  76         }
  77 
  78         return super.loadClass(className);
  79 
  80     }
  81 
  82     protected Class findClass(String name) throws ClassNotFoundException {
  83 
  84         StringBuilder sb = new StringBuilder(name.length() + 6);
  85         sb.append(name.replace('.','/')).append(".class");
  86 
  87         InputStream is = getResourceAsStream(sb.toString());
  88         if (is==null)
  89             throw new ClassNotFoundException("Class not found" + sb);
  90 
  91         try {
  92             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  93             byte[] buf = new byte[1024];
  94             int len;
  95             while((len=is.read(buf))>=0)
  96                 baos.write(buf,0,len);
  97 
  98             buf = baos.toByteArray();
  99 
 100             // define package if not defined yet
 101             int i = name.lastIndexOf('.');
 102             if (i != -1) {
 103                 String pkgname = name.substring(0, i);
 104                 Package pkg = getPackage(pkgname);
 105                 if(pkg==null)
 106                     definePackage(pkgname, null, null, null, null, null, null, null);
 107             }
 108 
 109             return defineClass(name,buf,0,buf.length);
 110         } catch (IOException e) {
 111             throw new ClassNotFoundException(name,e);
 112         } finally {
 113             try {
 114                 is.close();
 115             } catch (IOException ioe) {
 116                 //ignore
 117             }
 118         }
 119     }
 120 
 121     /**
 122      * Returns a class loader that can load classes from JDK tools.jar.
 123      * @param parent
 124      */
 125     private static URL[] getToolsJar(@Nullable ClassLoader parent) throws ToolsJarNotFoundException {
 126 
 127         try {
 128             Class.forName("com.sun.tools.javac.Main", false, parent);
 129             return new URL[0];
 130             // we can already load them in the parent class loader.
 131             // so no need to look for tools.jar.
 132             // this happens when we are run inside IDE/Ant, or
 133             // in Mac OS.
 134         } catch (ClassNotFoundException e) {
 135             // otherwise try to find tools.jar
 136         }
 137 
 138         File jreHome = new File(System.getProperty("java.home"));
 139         File toolsJar = new File( jreHome.getParent(), "lib/tools.jar" );
 140 
 141         if (!toolsJar.exists()) {
 142             throw new ToolsJarNotFoundException(toolsJar);
 143         }
 144 
 145         try {
 146             return new URL[]{toolsJar.toURL()};
 147         } catch (MalformedURLException e) {
 148             // impossible
 149             throw new AssertionError(e);
 150         }
 151     }
 152 }