View Javadoc

1   /*
2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3    * 
4    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5    * 
6    * Portions Copyright Apache Software Foundation.
7    * 
8    * The contents of this file are subject to the terms of either the GNU
9    * General Public License Version 2 only ("GPL") or the Common Development
10   * and Distribution License("CDDL") (collectively, the "License").  You
11   * may not use this file except in compliance with the License. You can obtain
12   * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
13   * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
14   * language governing permissions and limitations under the License.
15   * 
16   * When distributing the software, include this License Header Notice in each
17   * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
18   * Sun designates this particular file as subject to the "Classpath" exception
19   * as provided by Sun in the GPL Version 2 section of the License file that
20   * accompanied this code.  If applicable, add the following below the License
21   * Header, with the fields enclosed by brackets [] replaced by your own
22   * identifying information: "Portions Copyrighted [year]
23   * [name of copyright owner]"
24   * 
25   * Contributor(s):
26   * 
27   * If you wish your version of this file to be governed by only the CDDL or
28   * only the GPL Version 2, indicate your decision by adding "[Contributor]
29   * elects to include this software in this distribution under the [CDDL or GPL
30   * Version 2] license."  If you don't indicate a single choice of license, a
31   * recipient has the option to distribute your version of this file under
32   * either the CDDL, the GPL Version 2 or to extend the choice of license to
33   * its licensees as provided above.  However, if you add GPL Version 2 code
34   * and therefore, elected the GPL Version 2 license, then the option applies
35   * only if the new code is made subject to such option by the copyright
36   * holder.
37   */
38  
39  package org.apache.jasper.compiler;
40  
41  import java.io.BufferedOutputStream;
42  import java.io.BufferedReader;
43  import java.io.ByteArrayOutputStream;
44  import java.io.File;
45  import java.io.FileInputStream;
46  import java.io.FileNotFoundException;
47  import java.io.FileOutputStream;
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.io.InputStreamReader;
51  import java.io.OutputStreamWriter;
52  import java.io.Reader;
53  import java.io.Writer;
54  import java.io.UnsupportedEncodingException;
55  
56  import java.util.ArrayList;
57  import java.util.HashMap;
58  import java.util.List;
59  import java.util.Locale;
60  import java.util.Map;
61  import java.util.StringTokenizer;
62  import java.util.logging.Logger;
63  import java.util.logging.Level;
64  
65  import org.apache.jasper.JasperException;
66  import org.apache.jasper.JspCompilationContext;
67  
68  import org.eclipse.jdt.core.compiler.IProblem;
69  import org.eclipse.jdt.internal.compiler.ClassFile;
70  import org.eclipse.jdt.internal.compiler.CompilationResult;
71  import org.eclipse.jdt.internal.compiler.Compiler;
72  import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
73  import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
74  import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
75  import org.eclipse.jdt.internal.compiler.IProblemFactory;
76  import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
77  import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
78  import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
79  import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
80  import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
81  import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
82  
83  /**
84   * JDT class compiler. This compiler will load source dependencies from the
85   * context classloader, reducing dramatically disk access during 
86   * the compilation process.
87   *
88   * @author Cocoon2
89   * @author Remy Maucherat
90   * @author Kin-man Chung   Modified to implement JavaCompiler
91   */
92  
93  public class JDTJavaCompiler implements JavaCompiler {
94  
95      private final Map settings = new HashMap();
96      private JspCompilationContext ctxt;
97      private ErrorDispatcher errDispatcher;
98      private Logger log;
99      private String javaFileName;
100 
101 
102     public void init(JspCompilationContext ctxt,
103                      ErrorDispatcher errDispatcher,
104                      boolean suppressLogging) {
105         this.errDispatcher = errDispatcher;
106         this.ctxt = ctxt;
107         log = Logger.getLogger(JDTJavaCompiler.class.getName());
108         if (suppressLogging) {
109             log.setLevel(Level.OFF);
110         }
111     }
112 
113     public void setExtdirs(String exts) {
114         // no op here
115     }
116 
117     public void setClassPath(List<File> cpath) {
118        // No op here, because the current classloader is used.  However,
119        // This may not include the system classpath specified in options
120     }
121 
122     public long getClassLastModified() {
123         File classFile = new File(ctxt.getClassFileName());
124         return classFile.lastModified();
125     }
126 
127     public Writer getJavaWriter(String javaFileName,
128                                 String javaEncoding)
129             throws JasperException {
130 
131         this.javaFileName = javaFileName;
132 
133         Writer writer = null;
134         try {
135             writer = new OutputStreamWriter(
136                         new FileOutputStream(javaFileName), javaEncoding);
137         } catch (UnsupportedEncodingException ex) {
138             errDispatcher.jspError("jsp.error.needAlternateJavaEncoding",
139                                    javaEncoding);
140         } catch (IOException ex) {
141             errDispatcher.jspError("jsp.error.unableToCreateOutputWriter",
142                                    javaFileName, ex);
143         }
144         return writer;
145     }
146 
147     public void setDebug(boolean debug) {
148         settings.put(CompilerOptions.OPTION_LineNumberAttribute,
149                      CompilerOptions.GENERATE);
150         settings.put(CompilerOptions.OPTION_SourceFileAttribute,
151                      CompilerOptions.GENERATE);
152         settings.put(CompilerOptions.OPTION_ReportDeprecation,
153                      CompilerOptions.IGNORE);
154         if (debug) {
155             settings.put(CompilerOptions.OPTION_LocalVariableAttribute,
156                          CompilerOptions.GENERATE);
157         }
158     }
159 
160     public void setSourceVM(String sourceVM) {
161         if(sourceVM.equals("1.1")) {
162             settings.put(CompilerOptions.OPTION_Source,
163                          CompilerOptions.VERSION_1_1);
164         } else if(sourceVM.equals("1.2")) {
165             settings.put(CompilerOptions.OPTION_Source,
166                          CompilerOptions.VERSION_1_2);
167         } else if(sourceVM.equals("1.3")) {
168             settings.put(CompilerOptions.OPTION_Source,
169                          CompilerOptions.VERSION_1_3);
170         } else if(sourceVM.equals("1.4")) {
171             settings.put(CompilerOptions.OPTION_Source,
172                          CompilerOptions.VERSION_1_4);
173         } else if(sourceVM.equals("1.5")) {
174             settings.put(CompilerOptions.OPTION_Source,
175                          CompilerOptions.VERSION_1_5);
176         } else {
177             log.warning("Unknown source VM " + sourceVM + " ignored.");
178             settings.put(CompilerOptions.OPTION_Source,
179                     CompilerOptions.VERSION_1_5);
180         }
181     }
182 
183     public void setTargetVM(String targetVM) {
184         if(targetVM.equals("1.1")) {
185             settings.put(CompilerOptions.OPTION_TargetPlatform,
186                          CompilerOptions.VERSION_1_1);
187         } else if(targetVM.equals("1.2")) {
188             settings.put(CompilerOptions.OPTION_TargetPlatform,
189                          CompilerOptions.VERSION_1_2);
190         } else if(targetVM.equals("1.3")) {
191             settings.put(CompilerOptions.OPTION_TargetPlatform,
192                          CompilerOptions.VERSION_1_3);
193         } else if(targetVM.equals("1.4")) {
194             settings.put(CompilerOptions.OPTION_TargetPlatform,
195                          CompilerOptions.VERSION_1_4);
196         } else if(targetVM.equals("1.5")) {
197             settings.put(CompilerOptions.OPTION_TargetPlatform,
198                          CompilerOptions.VERSION_1_5);
199         } else {
200             log.warning("Unknown target VM " + targetVM + " ignored.");
201             settings.put(CompilerOptions.OPTION_TargetPlatform,
202                     CompilerOptions.VERSION_1_5);
203         }
204     }
205 
206     public void saveClassFile(String className, String classFileName) {
207         // class file are alwyas saved.
208     }
209 
210     public void doJavaFile(boolean keep) {
211         if (!keep) {
212             File javaFile = new File(javaFileName);
213             javaFile.delete();
214         }
215     }
216 
217     public JavacErrorDetail[] compile(final String targetClassName,
218                                       final Node.Nodes pageNodes)
219             throws JasperException {
220 
221         final String sourceFile = ctxt.getServletJavaFileName();
222         final String outputDir =
223             ctxt.getOptions().getScratchDir().getAbsolutePath();
224         String packageName = ctxt.getServletPackageName();
225 
226         final ClassLoader classLoader = ctxt.getJspLoader();
227         String[] fileNames = new String[] {sourceFile};
228         String[] classNames = new String[] {targetClassName};
229         final ArrayList<JavacErrorDetail> problemList =
230                 new ArrayList<JavacErrorDetail>();
231 
232         class CompilationUnit implements ICompilationUnit {
233 
234             String className;
235             String sourceFile;
236 
237             CompilationUnit(String sourceFile, String className) {
238                 this.className = className;
239                 this.sourceFile = sourceFile;
240             }
241 
242             public char[] getFileName() {
243                 return className.toCharArray();
244             }
245             
246             public char[] getContents() {
247                 char[] result = null;
248                 Reader reader = null;
249                 try {
250                     InputStreamReader isReader =
251                         new InputStreamReader(new FileInputStream(sourceFile),
252                                 ctxt.getOptions().getJavaEncoding());
253                     reader = new BufferedReader(isReader);
254                     if (reader != null) {
255                         char[] chars = new char[8192];
256                         StringBuffer buf = new StringBuffer();
257                         int count;
258                         while ((count = reader.read(chars, 0, 
259                                                     chars.length)) > 0) {
260                             buf.append(chars, 0, count);
261                         }
262                         result = new char[buf.length()];
263                         buf.getChars(0, result.length, result, 0);
264                     }
265                 } catch (IOException e) {
266                     log.log(Level.SEVERE, "Compilation error", e);
267                 } finally {
268                     if (reader != null) {
269                         try {
270                             reader.close();
271                         } catch (IOException ioe) {
272                             // Ignore
273                         }
274                     }
275                 }
276                 return result;
277             }
278             
279             public char[] getMainTypeName() {
280                 int dot = className.lastIndexOf('.');
281                 if (dot > 0) {
282                     return className.substring(dot + 1).toCharArray();
283                 }
284                 return className.toCharArray();
285             }
286             
287             public char[][] getPackageName() {
288                 StringTokenizer izer = 
289                     new StringTokenizer(className, ".");
290                 char[][] result = new char[izer.countTokens()-1][];
291                 for (int i = 0; i < result.length; i++) {
292                     String tok = izer.nextToken();
293                     result[i] = tok.toCharArray();
294                 }
295                 return result;
296             }
297         }
298 
299         final INameEnvironment env = new INameEnvironment() {
300 
301                 public NameEnvironmentAnswer 
302                     findType(char[][] compoundTypeName) {
303                     String result = "";
304                     String sep = "";
305                     for (int i = 0; i < compoundTypeName.length; i++) {
306                         result += sep;
307                         result += new String(compoundTypeName[i]);
308                         sep = ".";
309                     }
310                     return findType(result);
311                 }
312 
313                 public NameEnvironmentAnswer 
314                     findType(char[] typeName, 
315                              char[][] packageName) {
316                         String result = "";
317                         String sep = "";
318                         for (int i = 0; i < packageName.length; i++) {
319                             result += sep;
320                             result += new String(packageName[i]);
321                             sep = ".";
322                         }
323                         result += sep;
324                         result += new String(typeName);
325                         return findType(result);
326                 }
327                 
328                 private NameEnvironmentAnswer findType(String className) {
329 
330                     InputStream is = null;
331                     try {
332                         if (className.equals(targetClassName)) {
333                             ICompilationUnit compilationUnit = 
334                                 new CompilationUnit(sourceFile, className);
335                             return 
336                                 new NameEnvironmentAnswer(compilationUnit, null);
337                         }
338                         String resourceName = 
339                             className.replace('.', '/') + ".class";
340                         is = classLoader.getResourceAsStream(resourceName);
341                         if (is != null) {
342                             byte[] classBytes;
343                             byte[] buf = new byte[8192];
344                             ByteArrayOutputStream baos = 
345                                 new ByteArrayOutputStream(buf.length);
346                             int count;
347                             while ((count = is.read(buf, 0, buf.length)) > 0) {
348                                 baos.write(buf, 0, count);
349                             }
350                             baos.flush();
351                             classBytes = baos.toByteArray();
352                             char[] fileName = className.toCharArray();
353                             ClassFileReader classFileReader = 
354                                 new ClassFileReader(classBytes, fileName, 
355                                                     true);
356                             return 
357                                 new NameEnvironmentAnswer(classFileReader, null);
358                         }
359                     } catch (IOException exc) {
360                         log.log(Level.SEVERE, "Compilation error", exc);
361                     } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
362                         log.log(Level.SEVERE, "Compilation error", exc);
363                     } finally {
364                         if (is != null) {
365                             try {
366                                 is.close();
367                             } catch (IOException exc) {
368                                 // Ignore
369                             }
370                         }
371                     }
372                     return null;
373                 }
374 
375                 private boolean isPackage(String result) {
376                     if (result.equals(targetClassName)) {
377                         return false;
378                     }
379                     String resourceName = result.replace('.', '/') + ".class";
380                     InputStream is = 
381                         classLoader.getResourceAsStream(resourceName);
382                     return is == null;
383                 }
384 
385                 public boolean isPackage(char[][] parentPackageName, 
386                                          char[] packageName) {
387                     String result = "";
388                     String sep = "";
389                     if (parentPackageName != null) {
390                         for (int i = 0; i < parentPackageName.length; i++) {
391                             result += sep;
392                             String str = new String(parentPackageName[i]);
393                             result += str;
394                             sep = ".";
395                         }
396                     }
397                     String str = new String(packageName);
398                     if (Character.isUpperCase(str.charAt(0))) {
399                         if (!isPackage(result)) {
400                             return false;
401                         }
402                     }
403                     result += sep;
404                     result += str;
405                     return isPackage(result);
406                 }
407 
408                 public void cleanup() {
409                 }
410 
411             };
412 
413         final IErrorHandlingPolicy policy = 
414             DefaultErrorHandlingPolicies.proceedWithAllProblems();
415 
416         if (ctxt.getOptions().getJavaEncoding() != null) {
417             settings.put(CompilerOptions.OPTION_Encoding,
418                     ctxt.getOptions().getJavaEncoding());
419         }
420 
421         final IProblemFactory problemFactory = 
422             new DefaultProblemFactory(Locale.getDefault());
423         
424         final ICompilerRequestor requestor = new ICompilerRequestor() {
425                 public void acceptResult(CompilationResult result) {
426                     try {
427                         if (result.hasProblems()) {
428                             IProblem[] problems = result.getProblems();
429                             for (int i = 0; i < problems.length; i++) {
430                                 IProblem problem = problems[i];
431                                 if (problem.isError()) {
432                                     String name = 
433                                         new String(problems[i].getOriginatingFileName());
434                                     try {
435                                         problemList.add(
436                                             ErrorDispatcher.createJavacError(
437                                                 name,
438                                                 pageNodes,
439                                                 new StringBuffer(problem.getMessage()), 
440                                                 problem.getSourceLineNumber()));
441                                     } catch (JasperException e) {
442                                         log.log(Level.SEVERE, "Error visiting node", e);
443                                     }
444                                 }
445                             }
446                         }
447                         if (problemList.isEmpty()) {
448                             ClassFile[] classFiles = result.getClassFiles();
449                             for (int i = 0; i < classFiles.length; i++) {
450                                 ClassFile classFile = classFiles[i];
451                                 char[][] compoundName = 
452                                     classFile.getCompoundName();
453                                 String className = "";
454                                 String sep = "";
455                                 for (int j = 0; 
456                                      j < compoundName.length; j++) {
457                                     className += sep;
458                                     className += new String(compoundName[j]);
459                                     sep = ".";
460                                 }
461                                 byte[] bytes = classFile.getBytes();
462                                 String outFile = outputDir + "/" + 
463                                     className.replace('.', '/') + ".class";
464                                 FileOutputStream fout = 
465                                     new FileOutputStream(outFile);
466                                 BufferedOutputStream bos = 
467                                     new BufferedOutputStream(fout);
468                                 bos.write(bytes);
469                                 bos.close();
470                             }
471                         }
472                     } catch (IOException exc) {
473                         log.log(Level.SEVERE, "Compilation error", exc);
474                     }
475                 }
476             };
477 
478         ICompilationUnit[] compilationUnits = 
479             new ICompilationUnit[classNames.length];
480         for (int i = 0; i < compilationUnits.length; i++) {
481             compilationUnits[i] = new CompilationUnit(fileNames[i], classNames[i]);
482         }
483 
484         Compiler compiler = new Compiler(env,
485                                          policy,
486                                          settings,
487                                          requestor,
488                                          problemFactory);
489         compiler.compile(compilationUnits);
490 
491         if (problemList.isEmpty()) {
492             return null;
493         }
494         return problemList.toArray(new JavacErrorDetail[]{});
495     }
496     
497 }