1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.tool;
  28 
  29 import java.io.BufferedReader;
  30 import java.io.File;
  31 import java.io.IOException;
  32 import java.io.InputStreamReader;
  33 import java.io.PrintStream;
  34 import java.lang.reflect.Constructor;
  35 import java.lang.reflect.InvocationTargetException;
  36 import java.net.MalformedURLException;
  37 import java.net.URL;
  38 import java.net.URLClassLoader;
  39 import java.util.Enumeration;
  40 import java.util.HashSet;
  41 import java.util.Set;
  42 
  43 import com.sun.javatest.ResourceLoader;
  44 import com.sun.javatest.util.DynamicArray;
  45 import com.sun.javatest.util.I18NResourceBundle;
  46 import com.sun.javatest.util.StringArray;
  47 
  48 class ManagerLoader
  49 {
  50     ManagerLoader(Class managerClass, PrintStream log) {
  51         setManagerClass(managerClass);
  52         setLog(log);
  53     }
  54 
  55     void setManagerClass(Class managerClass) {
  56         this.managerClass = managerClass;
  57     }
  58 
  59     void setManagerConstructorArgs(Class[] argTypes, Object[] args) {
  60         constrArgTypes = argTypes;
  61         constrArgs = args;
  62     }
  63 
  64     void setLog(PrintStream log) {
  65         this.log = log;
  66     }
  67 
  68     Set<Object> loadManagers(String resourceName)
  69         throws IOException
  70     {
  71 
  72         Enumeration e = ResourceLoader.getResources(resourceName, getClass());
  73         Set<Object> mgrs = new HashSet<>();
  74         URLClassLoader altLoader = null;
  75 
  76         while (e.hasMoreElements()) {
  77             URL entry = (URL)(e.nextElement());
  78             try {
  79                 BufferedReader in = new BufferedReader(new InputStreamReader(entry.openStream()));
  80                 String line;
  81                 while ((line = in.readLine()) != null) {
  82                     line = line.trim();
  83                     if (line.length() == 0) {
  84                         continue;
  85                     } else if (line.startsWith("#")) {
  86                         line = line.substring(1).trim();
  87                         if (line.startsWith("classpath=")) {
  88                             line = line.substring(10).trim();
  89                             altLoader = getSecondaryLoader(line);
  90                             // note that only the last classpath line would be used
  91                         }
  92 
  93                         continue;
  94                     }   // elseif
  95 
  96                     try {
  97                         Object mgr = newInstance(this.getClass().getClassLoader(),
  98                                 line, entry);
  99                         if (mgr == null && altLoader != null) {
 100                             // attempt to load from the alternate class loader
 101                             mgr = newInstance(altLoader, line, entry);
 102                         }
 103 
 104                         if (mgr != null) {
 105                             mgrs.add(mgr);
 106                         } else {
 107                             if (log != null) {
 108                                 writeI18N("ml.cantFindClass", new Object[]{line, entry});
 109                             }
 110                         }
 111                     }
 112                     catch (IllegalAccessException ex) {
 113                         if (log != null) {
 114                             writeI18N("ml.cantAccessClass", new Object[] { line, entry } );
 115                         }
 116                     }
 117                     catch (InstantiationException ex) {
 118                         if (log != null) {
 119                             writeI18N("ml.cantCreateClass", new Object[] { line, entry } );
 120                         }
 121                     }
 122                     catch (NoSuchMethodException ex) {
 123                         if (log != null) {
 124                             writeI18N("ml.cantFindConstr", new Object[] { line, entry } );
 125                         }
 126                     }
 127                 }   // while
 128                 in.close();
 129             }
 130             catch (IOException ex) {
 131                 if (log != null)
 132                     writeI18N("ml.cantRead", new Object[] { entry, ex });
 133             }
 134         }
 135         return mgrs;
 136     }
 137 
 138     /**
 139      * Throws the declared exceptions because it seems that the class was found
 140      * but couldn't be used.  Not being able to find the class is not a problem,
 141      * this is handled in the code in other ways.
 142      * @param cl Class loader to use.
 143      * @param className Class to load.
 144      * @param specfile File which specified the classname to be loaded - used
 145      *     for error message purposes.
 146      * @return An instance of the requested class, null if not found.  Or if the
 147      *     object created is not a manager subclass.
 148      * @throws InstantiationException
 149      * @throws NoSuchMethodException
 150      * @throws IllegalAccessException
 151      */
 152     private Object newInstance(ClassLoader cl, String className, URL specfile)
 153         throws InstantiationException, NoSuchMethodException, IllegalAccessException {
 154         if (cl == null) {
 155             return null;
 156         }
 157 
 158         try {
 159             Class c = Class.forName(className, true, cl);
 160             Object mgr = newInstance(c);
 161             if (managerClass.isInstance(mgr)) {
 162                 return mgr;
 163             } else {
 164                 if (log != null) {
 165                     writeI18N("ml.notSubtype", new Object[]{className,
 166                             managerClass.getName(), specfile});
 167                 }
 168             }
 169         } catch (ClassNotFoundException ex) {
 170         }
 171 
 172         return null;
 173     }
 174 
 175     private Object newInstance(Class<?> c)
 176         throws IllegalAccessException, InstantiationException, NoSuchMethodException
 177     {
 178         if (constrArgTypes == null || constrArgTypes.length == 0) {
 179             return c.newInstance();
 180         }
 181 
 182         try {
 183             Constructor constr = c.getConstructor(constrArgTypes);
 184             return constr.newInstance(constrArgs);
 185         }
 186         catch (InvocationTargetException e) {
 187             Throwable t = e.getTargetException();
 188             if (t instanceof RuntimeException) {
 189                 throw ((RuntimeException) t);
 190             }
 191             else if (t instanceof Error) {
 192                 throw ((Error) t);
 193             }
 194             else {
 195                 throw new Error(e);
 196             }
 197         }
 198     }
 199 
 200     private URLClassLoader getSecondaryLoader(String paths) {
 201         String[] cps = StringArray.splitList(paths, ":");
 202 
 203         if (cps == null || cps.length == 0) {
 204             return null;
 205         }
 206 
 207         URL[] urls = new URL[0];
 208         boolean someCPok = false;
 209         for (String s : cps) {
 210             URL u = ResourceLoader.getExtUrl(new File(s));
 211 
 212             if (u != null) {
 213                 urls = DynamicArray.append(urls, u);
 214                 someCPok = true;
 215             }
 216         }   // for
 217 
 218         // none of the input classpath can be used
 219         if (!someCPok || urls == null || urls.length == 0) {
 220             if (log != null) {
 221                 log.println("Unable to create valid paths from any of the custom tool classpath items.");
 222             }
 223             return null;
 224         }
 225 
 226         // create CL
 227         try {
 228             URLClassLoader cl = new URLClassLoader(urls);
 229             return cl;
 230         } catch (SecurityException e) {
 231             if (log != null) {
 232                 log.println("Unable to create custom class loader to load tool managers:");
 233                 e.printStackTrace(log);
 234             }
 235             return null;
 236         }   // catch
 237     }
 238 
 239     private void writeI18N(String key, Object[] args) {
 240         log.println(i18n.getString(key, args));
 241     }
 242 
 243     private Class<?> managerClass;
 244     private Constructor constr;
 245     private Class[] constrArgTypes;
 246     private Object[] constrArgs;
 247     private PrintStream log;
 248 
 249     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(ManagerLoader.class);
 250 }