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 if(opt.equals("1.6")) {
304 settings.put(CompilerOptions.OPTION_Source,
305 CompilerOptions.VERSION_1_6);
306 } else if(opt.equals("1.7")) {
307 settings.put(CompilerOptions.OPTION_Source,
308 CompilerOptions.VERSION_1_7);
309 } else {
310 log.warn("Unknown source VM " + opt + " ignored.");
311 settings.put(CompilerOptions.OPTION_Source,
312 CompilerOptions.VERSION_1_5);
313 }
314 } else {
315 // Default to 1.5
316 settings.put(CompilerOptions.OPTION_Source,
317 CompilerOptions.VERSION_1_5);
318 }
319
320 // Target JVM
321 if(ctxt.getOptions().getCompilerTargetVM() != null) {
322 String opt = ctxt.getOptions().getCompilerTargetVM();
323 if(opt.equals("1.1")) {
324 settings.put(CompilerOptions.OPTION_TargetPlatform,
325 CompilerOptions.VERSION_1_1);
326 } else if(opt.equals("1.2")) {
327 settings.put(CompilerOptions.OPTION_TargetPlatform,
328 CompilerOptions.VERSION_1_2);
329 } else if(opt.equals("1.3")) {
330 settings.put(CompilerOptions.OPTION_TargetPlatform,
331 CompilerOptions.VERSION_1_3);
332 } else if(opt.equals("1.4")) {
333 settings.put(CompilerOptions.OPTION_TargetPlatform,
334 CompilerOptions.VERSION_1_4);
335 } else if(opt.equals("1.5")) {
336 settings.put(CompilerOptions.OPTION_TargetPlatform,
337 CompilerOptions.VERSION_1_5);
338 settings.put(CompilerOptions.OPTION_Compliance,
339 CompilerOptions.VERSION_1_5);
340 } else if(opt.equals("1.6")) {
341 settings.put(CompilerOptions.OPTION_TargetPlatform,
342 CompilerOptions.VERSION_1_6);
343 settings.put(CompilerOptions.OPTION_Compliance,
344 CompilerOptions.VERSION_1_6);
345 } else if(opt.equals("1.7")) {
346 settings.put(CompilerOptions.OPTION_TargetPlatform,
347 CompilerOptions.VERSION_1_7);
348 settings.put(CompilerOptions.OPTION_Compliance,
349 CompilerOptions.VERSION_1_7);
350 } else {
351 log.warn("Unknown target VM " + opt + " ignored.");
352 settings.put(CompilerOptions.OPTION_TargetPlatform,
353 CompilerOptions.VERSION_1_5);
354 }
355 } else {
356 // Default to 1.5
357 settings.put(CompilerOptions.OPTION_TargetPlatform,
358 CompilerOptions.VERSION_1_5);
359 settings.put(CompilerOptions.OPTION_Compliance,
360 CompilerOptions.VERSION_1_5);
361 }
362
363 final IProblemFactory problemFactory =
364 new DefaultProblemFactory(Locale.getDefault());
365
366 final ICompilerRequestor requestor = new ICompilerRequestor() {
367 public void acceptResult(CompilationResult result) {
368 try {
369 if (result.hasProblems()) {
370 IProblem[] problems = result.getProblems();
371 for (int i = 0; i < problems.length; i++) {
372 IProblem problem = problems[i];
373 if (problem.isError()) {
374 String name =
375 new String(problems[i].getOriginatingFileName());
376 try {
377 problemList.add(ErrorDispatcher.createJavacError
378 (name, pageNodes, new StringBuffer(problem.getMessage()),
379 problem.getSourceLineNumber(), ctxt));
380 } catch (JasperException e) {
381 log.error("Error visiting node", e);
382 }
383 }
384 }
385 }
386 if (problemList.isEmpty()) {
387 ClassFile[] classFiles = result.getClassFiles();
388 for (int i = 0; i < classFiles.length; i++) {
389 ClassFile classFile = classFiles[i];
390 char[][] compoundName =
391 classFile.getCompoundName();
392 String className = "";
393 String sep = "";
394 for (int j = 0;
395 j < compoundName.length; j++) {
396 className += sep;
397 className += new String(compoundName[j]);
398 sep = ".";
399 }
400 byte[] bytes = classFile.getBytes();
401 String outFile = outputDir + "/" +
402 className.replace('.', '/') + ".class";
403 FileOutputStream fout =
404 new FileOutputStream(outFile);
405 BufferedOutputStream bos =
406 new BufferedOutputStream(fout);
407 bos.write(bytes);
408 bos.close();
409 }
410 }
411 } catch (IOException exc) {
412 log.error("Compilation error", exc);
413 }
414 }
415 };
416
417 ICompilationUnit[] compilationUnits =
418 new ICompilationUnit[classNames.length];
419 for (int i = 0; i < compilationUnits.length; i++) {
420 String className = classNames[i];
421 compilationUnits[i] = new CompilationUnit(fileNames[i], className);
422 }
423 Compiler compiler = new Compiler(env,
424 policy,
425 settings,
426 requestor,
427 problemFactory,
428 true);
429 compiler.compile(compilationUnits);
430
431 if (!ctxt.keepGenerated()) {
432 File javaFile = new File(ctxt.getServletJavaFileName());
433 javaFile.delete();
434 }
435
436 if (!problemList.isEmpty()) {
437 JavacErrorDetail[] jeds =
438 (JavacErrorDetail[]) problemList.toArray(new JavacErrorDetail[0]);
439 errDispatcher.javacError(jeds);
440 }
441
442 if( log.isDebugEnabled() ) {
443 long t2=System.currentTimeMillis();
444 log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
445 + (t2-t1) + "ms");
446 }
447
448 if (ctxt.isPrototypeMode()) {
449 return;
450 }
451
452 // JSR45 Support
453 if (! options.isSmapSuppressed()) {
454 SmapUtil.installSmap(smap);
455 }
456
457 }
458
459
460 }