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