1   /*
2    *  Jdk.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Hamish Cunningham, 04/05/00
12   *
13   *  $Id: Jdk.java,v 1.14 2002/02/26 10:17:46 valyt Exp $
14   *
15   *  Developer notes:
16   *
17   *  It would be better to have a compile method that took two arrays
18   *  of string for a set of classes, and to compile all in one go
19   *
20   *  Also, should change gate.jape.RHS to use the methods here for
21   *  defining and instantiating classes.
22   *
23   * We might also try dispensing with javacompiler.jar and overriding
24   * File instead to take input from string, then using the normal
25   * Sun compiler.
26   */
27  
28  package gate.util;
29  
30  import java.io.*;
31  import java.lang.reflect.*;
32  import java.util.*;
33  
34  import sun.toolsx.java.*;
35  import sun.toolsx.javac.*;
36  
37  import gate.*;
38  
39  
40  /** Jdk encapsulates some utilities for poking around in your Java
41    * environment.
42    */
43  public class Jdk {
44  
45    /** Debug flag */
46    private static final boolean DEBUG = false;
47  
48    /** Anonymous construction. */
49    public Jdk() {
50    } // anon constructor
51  
52    /** main. */
53    public static void main(String[] args) throws GateException {
54      Jdk jdk = new Jdk();
55      jdk.testMe();
56    } // main
57  
58    /** Test method. Better to use TestJdk via the TestGate suite instead. */
59    public void testMe() throws GateException {
60      Out.println("Testing gate.util.Jdk");
61      Out.println("getToolsHome(): " + getToolsHome());
62    } // testMe
63  
64    /** Possible locations of the tools <CODE>bin</CODE> directory.
65      * (relative to "java.home").
66      */
67    private String[] toolsLocations = {
68      "/../bin",    // jdk1.2 final gives java.home as the jre directory)
69      "/bin",   // (jdk1.1 and 1.2 betas give java.home as the jdk directory)
70      "",     // getting desperate!
71      "/../../bin"  // e.g. we're running a JRE that's installed beside a JDK
72    };
73  
74  
75    private sun.toolsx.javac.Main compiler = null;
76    /** Returns a File specifying the location of the JDK tools, i.e.
77      * the location of programs like <CODE>java, javac, jar</CODE>. It
78      * assumes that if it finds <CODE>javac</CODE> or <CODE>javac.exe</CODE>
79      * then it found the tools home.
80      */
81    public File getToolsHome() throws GateException {
82      File javaHome = new File(System.getProperty("java.home"));
83      if(! javaHome.exists())
84        throw new GateException(
85          "directory " + javaHome.getPath() + " doesn't exist!"
86        );
87  
88      // try the likely spots
89      for(int i=0; i<toolsLocations.length; i++) {
90        try {
91          File javac = new
92            File(javaHome.getCanonicalPath() + toolsLocations[i] + "/javac");
93          if(javac.exists())
94            return new File(javaHome.getCanonicalPath() + toolsLocations[i]);
95          javac = new
96            File(javaHome.getCanonicalPath() + toolsLocations[i] + "/javac.exe");
97          if(javac.exists())
98            return new File(javaHome.getCanonicalPath() + toolsLocations[i]);
99        } catch(IOException e) {
100       }
101     }
102 
103     throw new GateException(
104       "Found no javac or javac.exe in likely places relative to java.home"
105     );
106   } // getToolsHome
107 
108   /** Compile a class from its source code string.
109     * @param className should have the package path to the source, e.g.
110     * com/thing/MyClass.java.
111     */
112   public byte[] compile(String javaCode, String className)
113   throws GateException {
114     if(compiler == null) compiler = new sun.toolsx.javac.Main(
115                                                   System.out, "gate.util.Jdk");
116     String argv[] = new String[5];
117     argv[0] = "-classpath";
118     argv[1] = System.getProperty("java.class.path");
119     argv[2] = "-nodisk";
120     argv[3] = className;
121     argv[4] = javaCode;
122 //long startTime = System.currentTimeMillis();
123     compiler.compile(argv);
124 //Out.prln("Compilation time: " + (System.currentTimeMillis() - startTime));
125     List compilerOutput = compiler.getCompilerOutput();
126 
127     Iterator iter = compilerOutput.iterator();
128     while(iter.hasNext()) {
129       byte[] classBytes = (byte[]) iter.next();
130 
131     if(classBytes == null || classBytes.length == 0)
132       throw new GateException("no bytes returned from compiler");
133 
134       // possibly this test is wrong - what about sources that contain
135       // multiple classes or have inner classes? at any rate we currently
136       // have no way to return them
137     if(iter.hasNext())
138       throw
139         new GateException("only compiled one class but got multiple results");
140 
141       return classBytes;
142     } // while
143 
144     throw new GateException("no compiler output");
145   } // compile(String, String)
146 
147 
148   /** Read the bytes for a class.
149     * @param classFileName should have the path to the .class
150     * file, e.g. com/thing/MyClass.class.
151     */
152   public byte[] readClass(String classFileName) throws GateException {
153     byte[] classBytes = null;
154     try {
155       File f = new File(classFileName);
156       FileInputStream fis = new FileInputStream(classFileName);
157       classBytes = new byte[(int) f.length()];
158       fis.read(classBytes, 0, (int) f.length());
159       fis.close();
160     } catch(IOException e) {
161       throw(new GateException("couldn't read class bytes: " + e));
162     }
163 
164     return classBytes;
165   } // readClass
166 
167   /** Load a class.
168     * @param classFileName is the path to the .class
169     * file, e.g. com/thing/MyClass.class.
170     */
171   public Class loadActionClass(String classFileName) throws GateException {
172     Class theClass = null;
173     try {
174       theClass = Gate.getClassLoader().loadClass(classFileName, true);
175     } catch(Exception e) {
176       e.printStackTrace();
177       throw new GateException(
178         "couldn't load " + classFileName + ": " + e.getMessage()
179       );
180     }
181 
182     return theClass;
183   } // loadActionClass
184 
185   /** Define a class from its qualified name and
186     * the byte array of its binary.
187     * @param classQualified name should be e.g. com.thing.MyClass.
188     * @param contains the bytes from a .class file.
189     */
190   public Class defineClass(String classQualifiedName, byte[] classBytes)
191   throws GateException {
192     Class theClass = null;
193     try {
194       theClass = Gate.getClassLoader().defineGateClass(
195         classQualifiedName, classBytes, 0, classBytes.length
196       );
197     } catch(ClassFormatError e) {
198       e.printStackTrace();
199       throw new GateException(
200         "couldn't define " + classQualifiedName + ": " + e
201       );
202 
203     }
204     Gate.getClassLoader().resolveGateClass(theClass);
205     return theClass;
206   } // defineClass
207 
208   /** Create an instance of a class. */
209   public Object instantiateClass(Class theClass) throws GateException {
210     Object theObject = null;
211     try {
212       theObject = theClass.newInstance();
213     } catch(Exception e) {
214       throw new GateException(
215         "couldn't create instance of class " + theClass + ": " + e
216       );
217     }
218 
219     return theObject;
220   } // instantiateClass
221 
222 } // Jdk
223 
224 
225 
226