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.13 2001/09/20 13:32:30 hamish 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    /** Returns a File specifying the location of the JDK tools, i.e.
75      * the location of programs like <CODE>java, javac, jar</CODE>. It
76      * assumes that if it finds <CODE>javac</CODE> or <CODE>javac.exe</CODE>
77      * then it found the tools home.
78      */
79    public File getToolsHome() throws GateException {
80      File javaHome = new File(System.getProperty("java.home"));
81      if(! javaHome.exists())
82        throw new GateException(
83          "directory " + javaHome.getPath() + " doesn't exist!"
84        );
85  
86      // try the likely spots
87      for(int i=0; i<toolsLocations.length; i++) {
88        try {
89          File javac = new
90            File(javaHome.getCanonicalPath() + toolsLocations[i] + "/javac");
91          if(javac.exists())
92            return new File(javaHome.getCanonicalPath() + toolsLocations[i]);
93          javac = new
94            File(javaHome.getCanonicalPath() + toolsLocations[i] + "/javac.exe");
95          if(javac.exists())
96            return new File(javaHome.getCanonicalPath() + toolsLocations[i]);
97        } catch(IOException e) {
98        }
99      }
100 
101     throw new GateException(
102       "Found no javac or javac.exe in likely places relative to java.home"
103     );
104   } // getToolsHome
105 
106   /** Compile a class from its source code string.
107     * @param className should have the package path to the source, e.g.
108     * com/thing/MyClass.java.
109     */
110   public byte[] compile(String javaCode, String className)
111   throws GateException {
112     sun.toolsx.javac.Main compiler = new sun.toolsx.javac.Main(
113       System.out, "gate.util.Jdk"
114     );
115     String argv[] = new String[5];
116     argv[0] = "-classpath";
117     argv[1] = System.getProperty("java.class.path");
118     argv[2] = "-nodisk";
119     argv[3] = className;
120     argv[4] = javaCode;
121     compiler.compile(argv);
122     List compilerOutput = compiler.getCompilerOutput();
123 
124     Iterator iter = compilerOutput.iterator();
125     while(iter.hasNext()) {
126       byte[] classBytes = (byte[]) iter.next();
127 
128     if(classBytes == null || classBytes.length == 0)
129       throw new GateException("no bytes returned from compiler");
130 
131       // possibly this test is wrong - what about sources that contain
132       // multiple classes or have inner classes? at any rate we currently
133       // have no way to return them
134     if(iter.hasNext())
135       throw
136         new GateException("only compiled one class but got multiple results");
137 
138       return classBytes;
139     } // while
140 
141     throw new GateException("no compiler output");
142   } // compile(String, String)
143 
144 
145   /** Read the bytes for a class.
146     * @param classFileName should have the path to the .class
147     * file, e.g. com/thing/MyClass.class.
148     */
149   public byte[] readClass(String classFileName) throws GateException {
150     byte[] classBytes = null;
151     try {
152       File f = new File(classFileName);
153       FileInputStream fis = new FileInputStream(classFileName);
154       classBytes = new byte[(int) f.length()];
155       fis.read(classBytes, 0, (int) f.length());
156       fis.close();
157     } catch(IOException e) {
158       throw(new GateException("couldn't read class bytes: " + e));
159     }
160 
161     return classBytes;
162   } // readClass
163 
164   /** Load a class.
165     * @param classFileName is the path to the .class
166     * file, e.g. com/thing/MyClass.class.
167     */
168   public Class loadActionClass(String classFileName) throws GateException {
169     Class theClass = null;
170     try {
171       theClass = Gate.getClassLoader().loadClass(classFileName, true);
172     } catch(Exception e) {
173       e.printStackTrace();
174       throw new GateException(
175         "couldn't load " + classFileName + ": " + e.getMessage()
176       );
177     }
178 
179     return theClass;
180   } // loadActionClass
181 
182   /** Define a class from its qualified name and
183     * the byte array of its binary.
184     * @param classQualified name should be e.g. com.thing.MyClass.
185     * @param contains the bytes from a .class file.
186     */
187   public Class defineClass(String classQualifiedName, byte[] classBytes)
188   throws GateException {
189     Class theClass = null;
190     try {
191       theClass = Gate.getClassLoader().defineGateClass(
192         classQualifiedName, classBytes, 0, classBytes.length
193       );
194     } catch(ClassFormatError e) {
195       e.printStackTrace();
196       throw new GateException(
197         "couldn't define " + classQualifiedName + ": " + e
198       );
199 
200     }
201     Gate.getClassLoader().resolveGateClass(theClass);
202     return theClass;
203   } // defineClass
204 
205   /** Create an instance of a class. */
206   public Object instantiateClass(Class theClass) throws GateException {
207     Object theObject = null;
208     try {
209       theObject = theClass.newInstance();
210     } catch(Exception e) {
211       throw new GateException(
212         "couldn't create instance of class " + theClass + ": " + e
213       );
214     }
215 
216     return theObject;
217   } // instantiateClass
218 
219 } // Jdk
220 
221 
222 
223