KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > java > JavaCompiler


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.java;
31
32 import com.caucho.bytecode.ByteCodeParser;
33 import com.caucho.bytecode.JavaClass;
34 import com.caucho.bytecode.SourceDebugExtensionAttribute;
35 import com.caucho.loader.DynamicClassLoader;
36 import com.caucho.log.Log;
37 import com.caucho.make.Make;
38 import com.caucho.server.util.CauchoSystem;
39 import com.caucho.util.CharBuffer;
40 import com.caucho.util.L10N;
41 import com.caucho.vfs.Encoding;
42 import com.caucho.vfs.IOExceptionWrapper;
43 import com.caucho.vfs.Path;
44 import com.caucho.vfs.ReadStream;
45 import com.caucho.vfs.WriteStream;
46
47 import java.io.IOException JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.Arrays JavaDoc;
50 import java.util.logging.Level JavaDoc;
51 import java.util.logging.Logger JavaDoc;
52 import java.util.regex.Pattern JavaDoc;
53
54 /**
55  * Compiles Java source, returning the loaded class.
56  */

57 public class JavaCompiler {
58   static final L10N L = new L10N(JavaCompiler.class);
59   static final Logger JavaDoc log = Log.open(JavaCompiler.class);
60   
61   private static final Object JavaDoc LOCK = new Object JavaDoc();
62
63   // Parent class loader. Used to grab the classpath.
64
private ClassLoader JavaDoc _loader;
65
66   // Executable name of the compiler
67
private String JavaDoc _compiler;
68
69   private String JavaDoc _sourceExt = ".java";
70   
71   private Path _classDir;
72   private Path _sourceDir;
73
74   private boolean _compileParent = true;
75
76   private String JavaDoc _extraClassPath;
77   private String JavaDoc _classPath;
78
79   protected String JavaDoc _charEncoding;
80   protected ArrayList JavaDoc<String JavaDoc> _args;
81
82   private int _maxBatch = 64;
83   protected long _maxCompileTime = 120 * 1000L;
84
85   private JavaCompiler()
86   {
87   }
88
89   /**
90    * Creates a new compiler.
91    *
92    * @param loader the parent class loader for the compiler.
93    */

94   public static JavaCompiler create()
95   {
96     return create(Thread.currentThread().getContextClassLoader());
97   }
98   
99   /**
100    * Creates a new compiler.
101    *
102    * @param loader the parent class loader for the compiler.
103    */

104   public static JavaCompiler create(ClassLoader JavaDoc loader)
105   {
106     JavacConfig config = JavacConfig.getLocalConfig();
107
108     String JavaDoc javac = config.getCompiler();
109
110     JavaCompiler javaCompiler = new JavaCompiler();
111
112     if (loader == null)
113       loader = Thread.currentThread().getContextClassLoader();
114
115     javaCompiler.setClassLoader(loader);
116     javaCompiler.setCompiler(javac);
117     javaCompiler.setArgs(config.getArgs());
118     javaCompiler.setEncoding(config.getEncoding());
119     javaCompiler.setMaxBatch(config.getMaxBatch());
120
121     return javaCompiler;
122   }
123
124   /**
125    * Sets the class loader used to load the compiled class and to grab
126    * the classpath from.
127    */

128   public void setClassLoader(ClassLoader JavaDoc loader)
129   {
130     _loader = loader;
131   }
132
133   /**
134    * Sets the class loader used to load the compiled class and to grab
135    * the classpath from.
136    */

137   public ClassLoader JavaDoc getClassLoader()
138   {
139     return _loader;
140   }
141
142   /**
143    * Sets the compiler name, e.g. jikes.
144    */

145   public void setCompiler(String JavaDoc compiler)
146   {
147     _compiler = compiler;
148   }
149
150   /**
151    * Gets the compiler name, e.g. jikes.
152    */

153   public String JavaDoc getCompiler()
154   {
155     if (_compiler == null)
156       _compiler = "javac";
157     
158     return _compiler;
159   }
160
161   /**
162    * Sets the directory where compiled classes go.
163    *
164    * @param path representing the class dir.
165    */

166   public void setClassDir(Path path)
167   {
168     try {
169       path.mkdirs();
170     } catch (IOException JavaDoc e) {
171     }
172     
173     _classDir = path;
174   }
175
176   /**
177    * Returns the directory where compiled classes go.
178    */

179   Path getClassDir()
180   {
181     if (_classDir != null)
182       return _classDir;
183     else
184       return CauchoSystem.getWorkPath();
185   }
186
187   /**
188    * Returns the path to the class directory.
189    */

190   String JavaDoc getClassDirName()
191   {
192     return getClassDir().getNativePath();
193   }
194
195   /**
196    * Sets the directory the java source comes from.
197    *
198    * @param path representing the source dir.
199    */

200   public void setSourceDir(Path path)
201   {
202     _sourceDir = path;
203   }
204
205   /**
206    * Returns the directory where compiled classes go.
207    */

208   public Path getSourceDir()
209   {
210     if (_sourceDir != null)
211       return _sourceDir;
212     else
213       return getClassDir();
214   }
215
216   /**
217    * Returns the path to the class directory.
218    */

219   String JavaDoc getSourceDirName()
220   {
221     return getSourceDir().getNativePath();
222   }
223
224   /**
225    * Sets the source extension.
226    */

227   public void setSourceExtension(String JavaDoc ext)
228   {
229     _sourceExt = ext;
230   }
231
232   /**
233    * Gets the source extension.
234    */

235   public String JavaDoc getSourceExtension()
236   {
237     return _sourceExt;
238   }
239
240   /**
241    * Sets the class path for compilation. Normally, the classpath from
242    * the class loader will be sufficient.
243    */

244   public void setClassPath(String JavaDoc classPath)
245   {
246     _classPath = classPath;
247   }
248
249   /**
250    * Sets an extra class path for compilation.
251    */

252   public void setExtraClassPath(String JavaDoc classPath)
253   {
254     _extraClassPath = classPath;
255   }
256
257   public void setCompileParent(boolean compileParent)
258   {
259     _compileParent = compileParent;
260   }
261   
262   /**
263    * Returns the classpath for the compiler.
264    */

265   public String JavaDoc getClassPath()
266   {
267     String JavaDoc classPath = null;//_classPath;
268

269     if (classPath != null)
270       return classPath;
271
272     if (classPath == null && _loader instanceof DynamicClassLoader) {
273       classPath = ((DynamicClassLoader) _loader).getClassPath();
274     }
275     else if (classPath == null)
276       classPath = CauchoSystem.getClassPath();
277
278     String JavaDoc srcDirName = getSourceDirName();
279     String JavaDoc classDirName = getClassDirName();
280     
281     char sep = CauchoSystem.getPathSeparatorChar();
282
283     if (_extraClassPath != null)
284       classPath = classPath + sep + _extraClassPath;
285
286     // Adding the srcDir lets javac and jikes find source files
287
if (! srcDirName.equals(classDirName))
288       classPath = srcDirName + sep + classPath;
289     classPath = classDirName + sep + classPath;
290
291     return classPath;
292   }
293
294   /**
295    * Sets any additional arguments for the compiler.
296    */

297   public void setArgs(String JavaDoc argString)
298   {
299     try {
300       if (argString != null) {
301     String JavaDoc []args = Pattern.compile("[\\s,]+").split(argString);
302
303     _args = new ArrayList JavaDoc<String JavaDoc>();
304
305     for (int i = 0; i < args.length; i++) {
306       if (! args[i].equals(""))
307         _args.add(args[i]);
308     }
309       }
310     } catch (Exception JavaDoc e) {
311       log.log(Level.WARNING, e.toString(), e);
312     }
313   }
314
315   /**
316    * Returns the ArrayList of arguments.
317    */

318   public ArrayList JavaDoc<String JavaDoc> getArgs()
319   {
320     return _args;
321   }
322
323   /**
324    * Sets the Java encoding for the compiler.
325    */

326   public void setEncoding(String JavaDoc encoding)
327   {
328     _charEncoding = encoding;
329
330     String JavaDoc javaEncoding = Encoding.getJavaName(encoding);
331     
332     if ("ISO8859_1".equals(javaEncoding))
333       _charEncoding = null;
334   }
335
336   /**
337    * Returns the encoding.
338    */

339   public String JavaDoc getEncoding()
340   {
341     return _charEncoding;
342   }
343
344   /**
345    * Returns the maximum time allowed for an external compilation.
346    */

347   public long getMaxCompileTime()
348   {
349     return _maxCompileTime;
350   }
351
352   /**
353    * Sets the maximum time allowed for an external compilation.
354    */

355   public void setMaxCompileTime(long maxCompileTime)
356   {
357     _maxCompileTime = maxCompileTime;
358   }
359
360   /**
361    * Sets the maximum time allowed for an external compilation.
362    */

363   public void setMaxBatch(int maxBatch)
364   {
365     _maxBatch = maxBatch;
366   }
367
368   /**
369    * Mangles the path into a valid Java class name.
370    */

371   public static String JavaDoc mangleName(String JavaDoc name)
372   {
373     boolean toLower = CauchoSystem.isCaseInsensitive();
374       
375     CharBuffer cb = new CharBuffer();
376     cb.append("_");
377
378     for (int i = 0; i < name.length(); i++) {
379       char ch = name.charAt(i);
380       
381       if (ch == '/' || ch == CauchoSystem.getPathSeparatorChar()) {
382         if (i == 0) {
383         }
384         else if (cb.charAt(cb.length() - 1) != '.' &&
385          (i + 1 < name.length() && name.charAt(i + 1) != '/'))
386           cb.append("._");
387       }
388       else if (ch == '.')
389         cb.append("__");
390       else if (ch == '_')
391         cb.append("_0");
392       else if (Character.isJavaIdentifierPart(ch))
393         cb.append(toLower ? Character.toLowerCase(ch) : ch);
394       else if (ch <= 256)
395         cb.append("_2" + encodeHex(ch >> 4) + encodeHex(ch));
396       else
397         cb.append("_4" + encodeHex(ch >> 12) + encodeHex(ch >> 8) +
398                   encodeHex(ch >> 4) + encodeHex(ch));
399     }
400
401     if (cb.length() == 0)
402       cb.append("_z");
403
404     return cb.toString();
405   }
406
407   private static char encodeHex(int i)
408   {
409     i &= 0xf;
410     
411     if (i < 10)
412       return (char) (i + '0');
413     else
414       return (char) (i - 10 + 'a');
415   }
416
417   public void setArgs(ArrayList JavaDoc<String JavaDoc> args)
418   {
419     if (args == null)
420       return;
421     if (_args == null)
422       _args = new ArrayList JavaDoc<String JavaDoc>();
423
424     _args.addAll(args);
425   }
426
427   /**
428    * Compiles the class. className is a fully qualified Java class, e.g.
429    * work.jsp.Test
430    *
431    * @param fileName Java source name -- in VFS format
432    * @param lineMap mapping from generated class back to the source class
433    *
434    * @return compiled class
435    */

436   public void compile(String JavaDoc fileName, LineMap lineMap)
437     throws IOException JavaDoc, ClassNotFoundException JavaDoc
438   {
439     compile(fileName, lineMap, false);
440   }
441
442   /**
443    * Compiles the class. className is a fully qualified Java class, e.g.
444    * work.jsp.Test
445    *
446    * @param fileName Java source name -- in VFS format
447    * @param lineMap mapping from generated class back to the source class
448    *
449    * @return compiled class
450    */

451   public void compileIfModified(String JavaDoc fileName, LineMap lineMap)
452     throws IOException JavaDoc, ClassNotFoundException JavaDoc
453   {
454     compile(fileName, lineMap, true);
455   }
456
457   /**
458    * Compiles the class. className is a fully qualified Java class, e.g.
459    * work.jsp.Test
460    *
461    * @param fileName Java source name -- in VFS format
462    * @param lineMap mapping from generated class back to the source class
463    * @param ifModified compile only if the *.java is modified
464    *
465    * @return compiled class
466    */

467   public void compile(String JavaDoc fileName, LineMap lineMap,
468                       boolean ifModified)
469     throws IOException JavaDoc, ClassNotFoundException JavaDoc
470   {
471     if (_compileParent) {
472       try {
473         if (_loader instanceof Make)
474           ((Make) _loader).make();
475       } catch (Exception JavaDoc e) {
476         throw new IOExceptionWrapper(e);
477       }
478     }
479
480     int p = fileName.lastIndexOf('.');
481     String JavaDoc path = fileName.substring(0, p);
482     String JavaDoc javaName = path + _sourceExt;
483     Path javaPath = getSourceDir().lookup(javaName);
484     
485     String JavaDoc className = path + ".class";
486     Path classPath = getClassDir().lookup(className);
487
488     synchronized (LOCK) {
489       if (ifModified &&
490           javaPath.getLastModified() <= classPath.getLastModified())
491         return;
492       
493       if (javaPath.canRead() && classPath.exists())
494         classPath.remove();
495
496       compileInt(new String JavaDoc[] { fileName }, lineMap);
497
498       // XXX: This is needed for some regressions to pass,
499
// basically the timing wouldn't work if the classpath time
500
// was selected by the compiler
501
// server/141d, server/10k0
502
// classPath.setLastModified(javaPath.getLastModified());
503
}
504   }
505
506   /**
507    * Compiles a batch list of classes.
508    *
509    * @return compiled class
510    */

511   public void compileBatch(String JavaDoc []files)
512     throws IOException JavaDoc, ClassNotFoundException JavaDoc
513   {
514     if (_compileParent) {
515       try {
516         if (_loader instanceof Make)
517           ((Make) _loader).make();
518       } catch (Exception JavaDoc e) {
519         throw new IOExceptionWrapper(e);
520       }
521     }
522
523     if (files.length == 0)
524       return;
525
526     // only batch a number of files at a time
527

528     int batchCount = _maxBatch;
529     if (batchCount < 0)
530       batchCount = Integer.MAX_VALUE / 2;
531     else if (batchCount == 0)
532       batchCount = 1;
533
534     IOException JavaDoc exn = null;
535
536     ArrayList JavaDoc<String JavaDoc> uniqueFiles = new ArrayList JavaDoc<String JavaDoc>();
537     for (int i = 0; i < files.length; i++) {
538       if (! uniqueFiles.contains(files[i]))
539     uniqueFiles.add(files[i]);
540     }
541     files = new String JavaDoc[uniqueFiles.size()];
542     uniqueFiles.toArray(files);
543
544     synchronized (LOCK) {
545       for (int i = 0; i < files.length; i += batchCount) {
546     int len = files.length - i;
547
548     if (batchCount < len)
549       len = batchCount;
550
551     String JavaDoc []batchFiles = new String JavaDoc[len];
552
553     System.arraycopy(files, i, batchFiles, 0, len);
554
555     Arrays.sort(batchFiles);
556
557     try {
558       compileInt(batchFiles, null);
559     } catch (IOException JavaDoc e) {
560       if (exn == null)
561         exn = e;
562       else
563         log.log(Level.WARNING, e.toString(), e);
564     }
565       }
566     }
567
568     if (exn != null)
569       throw exn;
570   }
571
572   protected void compileInt(String JavaDoc []path, LineMap lineMap)
573     throws IOException JavaDoc, JavaCompileException
574   {
575     AbstractJavaCompiler compiler;
576
577     for (int i = 0; i < path.length; i++)
578       log.config("Compiling " + path[i]);
579
580     if (_compiler.equals("internal"))
581       compiler = new InternalCompiler(this);
582     else if (_compiler.equals("eclipse"))
583       compiler = new EclipseCompiler(this);
584     else if (_compiler.equals("groovyc"))
585       compiler = new GroovyCompiler(this);
586     else
587       compiler = new ExternalCompiler(this);
588
589     compiler.setPath(path);
590     compiler.setLineMap(lineMap);
591
592     // the compiler may not be well-behaved enough to use the ThreadPool
593
Thread JavaDoc thread = new Thread JavaDoc(compiler);
594
595     thread.start();
596
597     synchronized (compiler) {
598       long endTime = System.currentTimeMillis() + _maxCompileTime;
599
600       while (! compiler.isDone() && System.currentTimeMillis() <= endTime) {
601     try {
602       compiler.wait(endTime - System.currentTimeMillis());
603     } catch (InterruptedException JavaDoc e) {
604       Thread.currentThread().interrupted();
605       log.log(Level.WARNING, e.toString(), e);
606     }
607       }
608     }
609
610     if (! compiler.isDone()) {
611       log.warning("compilation timed out");
612       thread.interrupt();
613       compiler.abort();
614     }
615
616     Throwable JavaDoc exn = compiler.getException();
617
618     if (exn == null) {
619     }
620     else if (exn instanceof IOException JavaDoc)
621       throw (IOException JavaDoc) exn;
622     else if (exn instanceof JavaCompileException)
623       throw (JavaCompileException) exn;
624     else if (exn instanceof RuntimeException JavaDoc)
625       throw (RuntimeException JavaDoc) exn;
626     else if (exn instanceof Error JavaDoc)
627       throw (Error JavaDoc) exn;
628     else
629       throw new IOExceptionWrapper(exn);
630
631     for (int i = 0; i < path.length; i++) {
632       Path javaPath = getSourceDir().lookup(path[i]);
633
634       if (! path[i].endsWith(".java"))
635     continue;
636
637       String JavaDoc className = path[i].substring(0, path[i].length() - 5) + ".class";
638       Path classPath = getClassDir().lookup(className);
639       Path smapPath = getSourceDir().lookup(path[i] + ".smap");
640
641       if (classPath.canRead() && smapPath.canRead())
642     mergeSmap(classPath, smapPath);
643     }
644   }
645
646   public void mergeSmap(Path classPath, Path smapPath)
647   {
648     try {
649       log.fine("merging .smap for " + classPath.getTail());
650       
651       ByteCodeParser parser = new ByteCodeParser();
652       JavaClass javaClass;
653       
654       ReadStream is = classPath.openRead();
655       try {
656     javaClass = parser.parse(is);
657       } finally {
658     is.close();
659       }
660
661       CharBuffer smap = new CharBuffer();
662
663       is = smapPath.openRead();
664       try {
665     int ch;
666
667     while ((ch = is.read()) >= 0) {
668       smap.append((char) ch);
669     }
670       } finally {
671     is.close();
672       }
673       
674       SourceDebugExtensionAttribute attr;
675
676       attr = new SourceDebugExtensionAttribute(smap.toString());
677
678       javaClass.addAttribute(attr);
679
680       WriteStream os = classPath.openWrite();
681       try {
682     javaClass.write(os);
683       } finally {
684     os.close();
685       }
686     } catch (Exception JavaDoc e) {
687       log.log(Level.WARNING, e.toString(), e);
688     }
689   }
690 }
691
Popular Tags