1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jasper.compiler;
19
20 import java.io.BufferedOutputStream;
21 import java.io.BufferedReader;
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.io.Reader;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.StringTokenizer;
36
37 import org.apache.jasper.JasperException;
38 import org.eclipse.jdt.core.compiler.IProblem;
39 import org.eclipse.jdt.internal.compiler.ClassFile;
40 import org.eclipse.jdt.internal.compiler.CompilationResult;
41 import org.eclipse.jdt.internal.compiler.Compiler;
42 import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
43 import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
44 import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
45 import org.eclipse.jdt.internal.compiler.IProblemFactory;
46 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
47 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
48 import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
49 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
50 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
51 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
52
53 /**
54 * JDT class compiler. This compiler will load source dependencies from the
55 * context classloader, reducing dramatically disk access during
56 * the compilation process.
57 *
58 * @author Cocoon2
59 * @author Remy Maucherat
60 */
61 public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
62
63
64 /**
65 * Compile the servlet from .java file to .class file
66 */
67 protected void generateClass(String[] smap)
68 throws FileNotFoundException, JasperException, Exception {
69
70 long t1 = 0;
71 if (log.isDebugEnabled()) {
72 t1 = System.currentTimeMillis();
73 }
74
75 final String sourceFile = ctxt.getServletJavaFileName();
76 final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
77 String packageName = ctxt.getServletPackageName();
78 final String targetClassName =
79 ((packageName.length() != 0) ? (packageName + ".") : "")
80 + ctxt.getServletClassName();
81 final ClassLoader classLoader = ctxt.getJspLoader();
82 String[] fileNames = new String[] {sourceFile};
83 String[] classNames = new String[] {targetClassName};
84 final ArrayList problemList = new ArrayList();
85
86 class CompilationUnit implements ICompilationUnit {
87
88 String className;
89 String sourceFile;
90
91 CompilationUnit(String sourceFile, String className) {
92 this.className = className;
93 this.sourceFile = sourceFile;
94 }
95
96 public char[] getFileName() {
97 return sourceFile.toCharArray();
98 }
99
100 public char[] getContents() {
101 char[] result = null;
102 FileInputStream is = null;
103 try {
104 is = new FileInputStream(sourceFile);
105 Reader reader =
106 new BufferedReader(new InputStreamReader(is, ctxt.getOptions().getJavaEncoding()));
107 if (reader != null) {
108 char[] chars = new char[8192];
109 StringBuffer buf = new StringBuffer();
110 int count;
111 while ((count = reader.read(chars, 0,
112 chars.length)) > 0) {
113 buf.append(chars, 0, count);
114 }
115 result = new char[buf.length()];
116 buf.getChars(0, result.length, result, 0);
117 }
118 } catch (IOException e) {
119 log.error("Compilation error", e);
120 } finally {
121 if (is != null) {
122 try {
123 is.close();
124 } catch (IOException exc) {
125 // Ignore
126 }
127 }
128 }
129 return result;
130 }
131
132 public char[] getMainTypeName() {
133 int dot = className.lastIndexOf('.');
134 if (dot > 0) {
135 return className.substring(dot + 1).toCharArray();
136 }
137 return className.toCharArray();
138 }
139
140 public char[][] getPackageName() {
141 StringTokenizer izer =
142 new StringTokenizer(className, ".");
143 char[][] result = new char[izer.countTokens()-1][];
144 for (int i = 0; i < result.length; i++) {
145 String tok = izer.nextToken();
146 result[i] = tok.toCharArray();
147 }
148 return result;
149 }
150 }
151
152 final INameEnvironment env = new INameEnvironment() {
153
154 public NameEnvironmentAnswer
155 findType(char[][] compoundTypeName) {
156 String result = "";
157 String sep = "";
158 for (int i = 0; i < compoundTypeName.length; i++) {
159 result += sep;
160 result += new String(compoundTypeName[i]);
161 sep = ".";
162 }
163 return findType(result);
164 }
165
166 public NameEnvironmentAnswer
167 findType(char[] typeName,
168 char[][] packageName) {
169 String result = "";
170 String sep = "";
171 for (int i = 0; i < packageName.length; i++) {
172 result += sep;
173 result += new String(packageName[i]);
174 sep = ".";
175 }
176 result += sep;
177 result += new String(typeName);
178 return findType(result);
179 }
180
181 private NameEnvironmentAnswer findType(String className) {
182
183 InputStream is = null;
184 try {
185 if (className.equals(targetClassName)) {
186 ICompilationUnit compilationUnit =
187 new CompilationUnit(sourceFile, className);
188 return
189 new NameEnvironmentAnswer(compilationUnit, null);
190 }
191 String resourceName =
192 className.replace('.', '/') + ".class";
193 is = classLoader.getResourceAsStream(resourceName);
194 if (is != null) {
195 byte[] classBytes;
196 byte[] buf = new byte[8192];
197 ByteArrayOutputStream baos =
198 new ByteArrayOutputStream(buf.length);
199 int count;
200 while ((count = is.read(buf, 0, buf.length)) > 0) {
201 baos.write(buf, 0, count);
202 }
203 baos.flush();
204 classBytes = baos.toByteArray();
205 char[] fileName = className.toCharArray();
206 ClassFileReader classFileReader =
207 new ClassFileReader(classBytes, fileName,
208 true);
209 return
210 new NameEnvironmentAnswer(classFileReader, null);
211 }
212 } catch (IOException exc) {
213 log.error("Compilation error", exc);
214 } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
215 log.error("Compilation error", exc);
216 } finally {
217 if (is != null) {
218 try {
219 is.close();
220 } catch (IOException exc) {
221 // Ignore
222 }
223 }
224 }
225 return null;
226 }
227
228 private boolean isPackage(String result) {
229 if (result.equals(targetClassName)) {
230 return false;
231 }
232 String resourceName = result.replace('.', '/') + ".class";
233 InputStream is =
234 classLoader.getResourceAsStream(resourceName);
235 return is == null;
236 }
237
238 public boolean isPackage(char[][] parentPackageName,
239 char[] packageName) {
240 String result = "";
241 String sep = "";
242 if (parentPackageName != null) {
243 for (int i = 0; i < parentPackageName.length; i++) {
244 result += sep;
245 String str = new String(parentPackageName[i]);
246 result += str;
247 sep = ".";
248 }
249 }
250 String str = new String(packageName);
251 if (Character.isUpperCase(str.charAt(0))) {
252 if (!isPackage(result)) {
253 return false;
254 }
255 }
256 result += sep;
257 result += str;
258 return isPackage(result);
259 }
260
261 public void cleanup() {
262 }
263
264 };
265
266 final IErrorHandlingPolicy policy =
267 DefaultErrorHandlingPolicies.proceedWithAllProblems();
268
269 final Map settings = new HashMap();
270 settings.put(CompilerOptions.OPTION_LineNumberAttribute,
271 CompilerOptions.GENERATE);
272 settings.put(CompilerOptions.OPTION_SourceFileAttribute,
273 CompilerOptions.GENERATE);
274 settings.put(CompilerOptions.OPTION_ReportDeprecation,
275 CompilerOptions.IGNORE);
276 if (ctxt.getOptions().getJavaEncoding() != null) {
277 settings.put(CompilerOptions.OPTION_Encoding,
278 ctxt.getOptions().getJavaEncoding());
279 }
280 if (ctxt.getOptions().getClassDebugInfo()) {
281 settings.put(CompilerOptions.OPTION_LocalVariableAttribute,
282 CompilerOptions.GENERATE);
283 }
284
285 // Source JVM
286 if(ctxt.getOptions().getCompilerSourceVM() != null) {
287 String opt = ctxt.getOptions().getCompilerSourceVM();
288 if(opt.equals("1.1")) {
289 settings.put(CompilerOptions.OPTION_Source,
290 CompilerOptions.VERSION_1_1);
291 } else if(opt.equals("1.2")) {
292 settings.put(CompilerOptions.OPTION_Source,
293 CompilerOptions.VERSION_1_2);
294 } else if(opt.equals("1.3")) {
295 settings.put(CompilerOptions.OPTION_Source,
296 CompilerOptions.VERSION_1_3);
297 } else if(opt.equals("1.4")) {
298 settings.put(CompilerOptions.OPTION_Source,
299 CompilerOptions.VERSION_1_4);
300 } else if(opt.equals("1.5")) {
301 settings.put(CompilerOptions.OPTION_Source,
302 CompilerOptions.VERSION_1_5);
303 } else {
304 log.warn("Unknown source VM " + opt + " ignored.");
305 settings.put(CompilerOptions.OPTION_Source,
306 CompilerOptions.VERSION_1_5);
307 }
308 } else {
309 // Default to 1.5
310 settings.put(CompilerOptions.OPTION_Source,
311 CompilerOptions.VERSION_1_5);
312 }
313
314 // Target JVM
315 if(ctxt.getOptions().getCompilerTargetVM() != null) {
316 String opt = ctxt.getOptions().getCompilerTargetVM();
317 if(opt.equals("1.1")) {
318 settings.put(CompilerOptions.OPTION_TargetPlatform,
319 CompilerOptions.VERSION_1_1);
320 } else if(opt.equals("1.2")) {
321 settings.put(CompilerOptions.OPTION_TargetPlatform,
322 CompilerOptions.VERSION_1_2);
323 } else if(opt.equals("1.3")) {
324 settings.put(CompilerOptions.OPTION_TargetPlatform,
325 CompilerOptions.VERSION_1_3);
326 } else if(opt.equals("1.4")) {
327 settings.put(CompilerOptions.OPTION_TargetPlatform,
328 CompilerOptions.VERSION_1_4);
329 } else if(opt.equals("1.5")) {
330 settings.put(CompilerOptions.OPTION_TargetPlatform,
331 CompilerOptions.VERSION_1_5);
332 settings.put(CompilerOptions.OPTION_Compliance,
333 CompilerOptions.VERSION_1_5);
334 } else {
335 log.warn("Unknown target VM " + opt + " ignored.");
336 settings.put(CompilerOptions.OPTION_TargetPlatform,
337 CompilerOptions.VERSION_1_5);
338 }
339 } else {
340 // Default to 1.5
341 settings.put(CompilerOptions.OPTION_TargetPlatform,
342 CompilerOptions.VERSION_1_5);
343 settings.put(CompilerOptions.OPTION_Compliance,
344 CompilerOptions.VERSION_1_5);
345 }
346
347 final IProblemFactory problemFactory =
348 new DefaultProblemFactory(Locale.getDefault());
349
350 final ICompilerRequestor requestor = new ICompilerRequestor() {
351 public void acceptResult(CompilationResult result) {
352 try {
353 if (result.hasProblems()) {
354 IProblem[] problems = result.getProblems();
355 for (int i = 0; i < problems.length; i++) {
356 IProblem problem = problems[i];
357 if (problem.isError()) {
358 String name =
359 new String(problems[i].getOriginatingFileName());
360 try {
361 problemList.add(ErrorDispatcher.createJavacError
362 (name, pageNodes, new StringBuffer(problem.getMessage()),
363 problem.getSourceLineNumber(), ctxt));
364 } catch (JasperException e) {
365 log.error("Error visiting node", e);
366 }
367 }
368 }
369 }
370 if (problemList.isEmpty()) {
371 ClassFile[] classFiles = result.getClassFiles();
372 for (int i = 0; i < classFiles.length; i++) {
373 ClassFile classFile = classFiles[i];
374 char[][] compoundName =
375 classFile.getCompoundName();
376 String className = "";
377 String sep = "";
378 for (int j = 0;
379 j < compoundName.length; j++) {
380 className += sep;
381 className += new String(compoundName[j]);
382 sep = ".";
383 }
384 byte[] bytes = classFile.getBytes();
385 String outFile = outputDir + "/" +
386 className.replace('.', '/') + ".class";
387 FileOutputStream fout =
388 new FileOutputStream(outFile);
389 BufferedOutputStream bos =
390 new BufferedOutputStream(fout);
391 bos.write(bytes);
392 bos.close();
393 }
394 }
395 } catch (IOException exc) {
396 log.error("Compilation error", exc);
397 }
398 }
399 };
400
401 ICompilationUnit[] compilationUnits =
402 new ICompilationUnit[classNames.length];
403 for (int i = 0; i < compilationUnits.length; i++) {
404 String className = classNames[i];
405 compilationUnits[i] = new CompilationUnit(fileNames[i], className);
406 }
407 Compiler compiler = new Compiler(env,
408 policy,
409 settings,
410 requestor,
411 problemFactory,
412 true);
413 compiler.compile(compilationUnits);
414
415 if (!ctxt.keepGenerated()) {
416 File javaFile = new File(ctxt.getServletJavaFileName());
417 javaFile.delete();
418 }
419
420 if (!problemList.isEmpty()) {
421 JavacErrorDetail[] jeds =
422 (JavacErrorDetail[]) problemList.toArray(new JavacErrorDetail[0]);
423 errDispatcher.javacError(jeds);
424 }
425
426 if( log.isDebugEnabled() ) {
427 long t2=System.currentTimeMillis();
428 log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
429 + (t2-t1) + "ms");
430 }
431
432 if (ctxt.isPrototypeMode()) {
433 return;
434 }
435
436 // JSR45 Support
437 if (! options.isSmapSuppressed()) {
438 SmapUtil.installSmap(smap);
439 }
440
441 }
442
443
444 }