Javac.java
001 /*
002  
003  * Copyright (c) 1995-2012, The University of Sheffield. See the file
004  * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
005  
006  * This file is part of GATE (see http://gate.ac.uk/), and is free software,
007  * licenced under the GNU Library General Public License, Version 2, June 1991
008  * (in the distribution as file licence.html, and also available at
009  * http://gate.ac.uk/gate/licence.html).
010  
011  * Valentin Tablan, 18/Feb/2002
012  
013  * $Id: Javac.java 17396 2014-02-22 10:16:45Z markagreenwood $
014  */
015 package gate.util;
016 
017 import gate.Gate;
018 import gate.GateConstants;
019 
020 import java.lang.reflect.Constructor;
021 import java.lang.reflect.InvocationTargetException;
022 import java.util.Map;
023 
024 /**
025  * This class compiles a set of java sources using the user's preferred Java
026  * compiler. The default compiler used is the Eclipse JDT compiler, but this can
027  * be overridden by the user via an option in gate.xml.
028  */
029 public abstract class Javac implements GateConstants {
030 
031   /**
032    * Compiles a set of java sources and loads the compiled classes in the gate
033    * class loader.
034    
035    @param sources
036    *          a map from fully qualified classname to java source
037    @param classLoader
038    *          the classloader into which the sources should be compiled and
039    *          loaded. Note that this classloader must also have access to all
040    *          the classes required to compile the sources.
041    @throws GateException
042    *           in case of a compilation error or warning. In the case of
043    *           warnings the compiled classes are loaded before the error is
044    *           raised.
045    */
046   public static void loadClasses(Map<String, String> sources,
047       GateClassLoader classLoaderthrows GateException {
048     
049     if(compiler == null) {
050       setCompilerTypeFromUserConfig();
051     }
052     
053     if (classLoader == null) {
054       Err.println("A null classloader was provided, using the Top-Level GATE classloader instead!");
055       classLoader = Gate.getClassLoader();
056     }
057 
058     compiler.compile(sources, classLoader);
059   }
060 
061   /**
062    * Sets the type of compiler to be used, based on the user's configuration.
063    * The default is to use the Eclipse compiler unless the user requests
064    * otherwise.
065    */
066   private static void setCompilerTypeFromUserConfig() throws GateException {
067     // see if the user has expressed a preference
068     String compilerType = Gate.getUserConfig().getString(COMPILER_TYPE_KEY);
069     // if not, use the default
070     if(compilerType == null) {
071       compilerType = DEFAULT_COMPILER;
072     }
073 
074     // We try and load the compiler class first by treating the given
075     // name as a fully qualified class name. If this fails, we prepend
076     // "gate.util.compilers." (so the user can say just "Sun" rather
077     // than "gate.util.compilers.Sun"). If that fails, we try the
078     // default value DEFAULT_COMPILER. If that fails, we give up.
079     try {
080       // first treat the compiler type as a fully qualified class name
081       compiler = createCompilerInstance(compilerType);
082     catch(GateException ge) {
083       // if it's the default compiler we've just failed to load, give up
084       // now
085       if(DEFAULT_COMPILER.equals(compilerType)) { throw ge; }
086 
087       // we failed to find the class as a FQN, so try relative to
088       // gate.util.compilers
089       compilerType = "gate.util.compilers." + compilerType;
090       try {
091         compiler = createCompilerInstance(compilerType);
092       catch(GateException ge2) {
093         // if it's the default compiler we've just failed to load, give
094         // up now
095         if(DEFAULT_COMPILER.equals(compilerType)) { throw ge2; }
096 
097         Err.prln("Unable to load compiler class " + compilerType
098             ", falling back to default of " + DEFAULT_COMPILER);
099         compilerType = DEFAULT_COMPILER;
100         // last try - fall back on the default value. If this fails we
101         // just allow the failure exception to propagate up the stack
102         // from here.
103         compiler = createCompilerInstance(compilerType);
104       }
105     }
106   }
107 
108   private static Javac createCompilerInstance(String compilerType)
109       throws GateException {
110     Class<?> compilerClass = null;
111     try {
112       // first treat the compiler type as a fully qualified class name
113       compilerClass = Gate.getClassLoader().loadClass(compilerType, true);
114     catch(ClassNotFoundException cnfe) {
115       // ignore exception but leave compilerClass == null
116     }
117 
118     if(compilerClass == null || !Javac.class.isAssignableFrom(compilerClass)) { throw new GateException(
119         "Unable to load Java compiler class " + compilerType)}
120 
121     // At this point we have successfully loaded a compiler class.
122     // Now try and create an instance using a no-argument constructor.
123     try {
124       Constructor<?> noArgConstructor = compilerClass.getConstructor();
125       return (Javac)noArgConstructor.newInstance();
126     catch(IllegalAccessException iae) {
127       throw new GateException("Cannot access Java compiler class "
128           + compilerType, iae);
129     catch(InstantiationException ie) {
130       throw new GateException("Cannot instantiate Java compiler class "
131           + compilerType, ie);
132     catch(NoSuchMethodException nsme) {
133       throw new GateException("Java compiler class " + compilerType
134           " does not have a no-argument constructor", nsme);
135     catch(InvocationTargetException ite) {
136       throw new GateException("Exception when constructing Java compiler "
137           "of type " + compilerType, ite.getCause());
138     catch(ExceptionInInitializerError eiie) {
139       throw new GateException("Exception when initializing Java compiler "
140           "class " + compilerType, eiie.getCause());
141     }
142   }
143 
144   /**
145    * Compile a set of Java sources, and load the resulting classes into the GATE
146    * class loader.
147    
148    @param sources
149    *          a map from fully qualified classname to java source
150    @param classLoader
151    *          the classloader into which the sources should be compiled. Note
152    *          that this classloader must also have access to all the classes
153    *          required to compile the sources.
154    @throws GateException
155    *           in case of a compilation error or warning. In the case of
156    *           warnings, the compiled classes are loaded before the exception is
157    *           thrown.
158    */
159   public abstract void compile(Map<String, String> sources,
160       GateClassLoader classLoaderthrows GateException;
161 
162   /**
163    * The compiler to use.
164    */
165   private static Javac compiler = null;
166 
167   /**
168    * The default compiler to use.
169    */
170   public static final String DEFAULT_COMPILER = "gate.util.compilers.Eclipse";
171 }