1 /***************************************************************************************
2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.compiler;
9
10 import org.codehaus.aspectwerkz.definition.DefinitionLoader;
11 import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
12 import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
13 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
14 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
15 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
16 import org.codehaus.aspectwerkz.joinpoint.management.AdviceInfoContainer;
17 import org.codehaus.aspectwerkz.util.ContextClassLoader;
18 import org.codehaus.aspectwerkz.aspect.AdviceInfo;
19 import org.codehaus.aspectwerkz.cflow.CflowBinding;
20 import org.codehaus.aspectwerkz.cflow.CflowCompiler;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.Enumeration;
35 import java.util.HashMap;
36 import java.util.Hashtable;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.jar.Attributes;
41 import java.util.jar.Manifest;
42 import java.util.zip.CRC32;
43 import java.util.zip.ZipEntry;
44 import java.util.zip.ZipFile;
45 import java.util.zip.ZipOutputStream;
46
47 /***
48 * AspectWerkzC allow for precompilation of class / jar / zip given a class preprocessor. <p/>
49 * <h2>Usage</h2>
50 * <p/>
51 * <pre>
52 * java [-Daspectwerkz.classloader.preprocessor={ClassPreProcessorImpl}] -cp [...]
53 * org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] [-genjp] [-details] [-cp {additional cp i}]* {target
54 * 1} .. {target n}
55 * {ClassPreProcessorImpl} : full qualified name of the ClassPreProcessor implementation (must be in classpath)
56 * defaults to org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor
57 * {additional cp i} : additionnal classpath needed at compile time (eg: myaspect.jar)
58 * use as many -cp options as needed
59 * supports java classpath syntax for classpath separator: ; on windows, : on others
60 * {target i} : exploded dir, jar, zip files to compile
61 * Ant 1.5 must be in the classpath
62 * </pre>
63 * <p/>
64 * <p>
65 * <h2>Classpath note</h2>
66 * At the beginning of the compilation, all {target i} are added to the classpath automatically. <br/>This is required
67 * to support caller side advices. <p/>
68 * <h2>Error handling</h2>
69 * For each target i, a backup copy is written in ./_aspectwerkzc/i/target <br/>Transformation occurs on original target
70 * class/dir/jar/zip file <br/>On failure, target backup is restored and stacktrace is given <br/><br/>If
71 * <i>-haltOnError </i> was set, compilations ends and a <b>complete </b> rollback occurs on all targets, else a status
72 * report is printed at the end of the compilation, indicating SUCCESS or ERROR for each given target. <br/>If
73 * <i>-verify </i> was set, all compiled class are verified during the compilation and an error is generated if the
74 * compiled class bytecode is corrupted. The error is then handled according to the <i>-haltOnError </i> option. <br/>
75 * <p/>
76 * <h2>Manifest.mf update</h2>
77 * The Manifest.mf if present is updated wit the following:
78 * <ul>
79 * <li>AspectWerkzC-created: date of the compilation</li>
80 * <li>AspectWerkzC-preprocessor: full qualified classname of the preprocessor used</li>
81 * <li>AspectWerkzC-comment: comments</li>
82 * </ul>
83 *
84 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
85 */
86 public class AspectWerkzC {
87
88 private static final String COMMAND_LINE_OPTION_DASH = "-";
89 private static final String COMMAND_LINE_OPTION_VERBOSE = "-verbose";
90 private static final String COMMAND_LINE_OPTION_DETAILS = "-details";
91 private static final String COMMAND_LINE_OPTION_GENJP = "-genjp";
92 private static final String COMMAND_LINE_OPTION_HALT = "-haltOnError";
93 private static final String COMMAND_LINE_OPTION_VERIFY = "-verify";
94 private static final String COMMAND_LINE_OPTION_CLASSPATH = "-cp";
95 private static final String COMMAND_LINE_OPTION_TARGETS = "compile.targets";
96
97 /***
98 * option used to defined the class preprocessor
99 */
100 private static final String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
101
102 private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
103
104 /***
105 * default class preprocessor
106 */
107 private static final String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
108
109 private final static String MF_CUSTOM_DATE = "X-AspectWerkzC-created";
110
111 private final static String MF_CUSTOM_PP = "X-AspectWerkzC-preprocessor";
112
113 private final static String MF_CUSTOM_COMMENT = "X-AspectWerkzC-comment";
114
115 private final static String MF_CUSTOM_COMMENT_VALUE = "AspectWerkzC - AspectWerkz compiler, aspectwerkz.codehaus.org";
116
117 private final static SimpleDateFormat DF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
118
119 private final static String BACKUP_DIR = "_aspectwerkzc";
120
121 private boolean verify = false;
122
123 private boolean genJp = false;
124
125 private boolean haltOnError = false;
126
127 private String backupDir = BACKUP_DIR;
128
129 /***
130 * class loader in which the effective compilation occurs, child of system classloader
131 */
132 private URLClassLoader compilationLoader = null;
133
134 /***
135 * class preprocessor instance used to compile targets
136 */
137 private ClassPreProcessor preprocessor = null;
138 private boolean isAspectWerkzPreProcessor = false;
139
140 /***
141 * index to keep track of {target i} backups
142 */
143 private int sourceIndex;
144
145 /***
146 * Maps the target file to the target backup file
147 */
148 private Map backupMap = new HashMap();
149
150 /***
151 * Maps the target file to a status indicating compilation was successfull
152 */
153 private Map successMap = new HashMap();
154
155 private long timer;
156
157 /***
158 * Utility for file manipulation
159 */
160 private Utility utility;
161
162 /***
163 * Construct a new Utility, restore the index for backup
164 */
165 public AspectWerkzC() {
166
167 sourceIndex = 0;
168 utility = new Utility();
169 timer = System.currentTimeMillis();
170 }
171
172
173
174
175
176 public void setVerbose(boolean verbose) {
177 utility.setVerbose(verbose);
178 }
179
180 public void setGenJp(boolean genpJp) {
181 this.genJp = genpJp;
182 }
183
184 public void setHaltOnError(boolean haltOnError) {
185 this.haltOnError = haltOnError;
186 }
187
188 public void setVerify(boolean verify) {
189 this.verify = verify;
190 }
191
192 public void setDetails(boolean details) {
193 if (details) {
194 System.setProperty(AW_TRANSFORM_DETAILS, "true");
195 }
196 }
197
198 public void setBackupDir(String backup) {
199 this.backupDir = backup;
200 }
201
202 public Utility getUtility() {
203 return utility;
204 }
205
206 /***
207 * Sets the ClassPreProcessor implementation to use. <p/>The ClassLoader will be set to System ClassLoader when
208 * transform(className, byteCode, callerClassLoader) will be called to compile a class.
209 */
210 public void setPreprocessor(String preprocessor) throws CompileException {
211 try {
212 Class pp = Class.forName(preprocessor);
213 this.preprocessor = (ClassPreProcessor) pp.newInstance();
214 this.preprocessor.initialize();
215
216 if (this.preprocessor instanceof AspectWerkzPreProcessor) {
217 isAspectWerkzPreProcessor = true;
218 }
219 } catch (Exception e) {
220 throw new CompileException("failed to instantiate preprocessor " + preprocessor, e);
221 }
222 }
223
224 /***
225 * Backup source file in backup_dir/index/file. The backupMap is updated for further rollback
226 */
227 public void backup(File source, int index) {
228
229 File dest = new File(this.backupDir + File.separator + index + File.separator + source.getName());
230 utility.backupFile(source, dest);
231
232
233 backupMap.put(source, dest);
234 }
235
236 /***
237 * Restore the backup registered
238 */
239 public void restoreBackup() {
240 for (Iterator i = backupMap.keySet().iterator(); i.hasNext();) {
241 File source = (File) i.next();
242 if (!successMap.containsKey(source)) {
243 File dest = (File) backupMap.get(source);
244 utility.backupFile(dest, source);
245 }
246 }
247 }
248
249 /***
250 * Delete backup dir at the end of all compilation
251 */
252 public void postCompile(String message) {
253 restoreBackup();
254 utility.log(" [backup] removing backup");
255 utility.deleteDir(new File(this.backupDir));
256 long ms = Math.max(System.currentTimeMillis() - timer, 1 * 1000);
257 System.out.println("( " + (int) (ms / 1000) + " s ) " + message);
258 if (!haltOnError) {
259 for (Iterator i = backupMap.keySet().iterator(); i.hasNext();) {
260 File source = (File) i.next();
261 if (successMap.containsKey(source)) {
262 System.out.println("SUCCESS: " + source);
263 } else {
264 System.out.println("FAILED : " + source);
265 }
266 }
267 }
268 }
269
270 /***
271 * Compile sourceFile. If prefixPackage is not null, assumes it is the class package information. <p/>Handles :
272 * <ul>
273 * <li>directory recursively (exploded jar)</li>
274 * <li>jar / zip file</li>
275 * </ul>
276 */
277 public void doCompile(File sourceFile, String prefixPackage) throws CompileException {
278 if (sourceFile.isDirectory()) {
279 File[] classes = sourceFile.listFiles();
280 for (int i = 0; i < classes.length; i++) {
281 if (classes[i].isDirectory() && !(this.backupDir.equals(classes[i].getName()))) {
282 String packaging = (prefixPackage != null) ? (prefixPackage + "." + classes[i]
283 .getName()) : classes[i].getName();
284 doCompile(classes[i], packaging);
285 } else if (classes[i].getName().toLowerCase().endsWith(".class")) {
286 compileClass(classes[i], prefixPackage);
287 } else if (isJarFile(classes[i])) {
288
289 compileJar(classes[i]);
290 }
291 }
292 } else if (sourceFile.getName().toLowerCase().endsWith(".class")) {
293 compileClass(sourceFile, null);
294 } else if (isJarFile(sourceFile)) {
295 compileJar(sourceFile);
296 }
297 }
298
299 /***
300 * Compiles .class file using fileName as className and given packaging as package name
301 */
302 public void compileClass(File file, String packaging) throws CompileException {
303 InputStream in = null;
304 FileOutputStream fos = null;
305 try {
306 utility.log(" [compile] " + file.getCanonicalPath());
307
308
309 ByteArrayOutputStream bos = new ByteArrayOutputStream();
310 in = new FileInputStream(file);
311 byte[] buffer = new byte[1024];
312 while (in.available() > 0) {
313 int length = in.read(buffer);
314 if (length == -1) {
315 break;
316 }
317 bos.write(buffer, 0, length);
318 }
319
320
321 String className = file.getName().substring(0, file.getName().length() - 6);
322 if (packaging != null) {
323 className = packaging + '.' + className;
324 }
325
326
327 AspectWerkzPreProcessor.Output out = null;
328 try {
329 out = preProcess(preprocessor, className, bos.toByteArray(), compilationLoader);
330 } catch (Throwable t) {
331 throw new CompileException("weaver failed for class: " + className, t);
332 }
333
334
335 fos = new FileOutputStream(file);
336 fos.write(out.bytecode);
337 fos.close();
338
339
340 if (out.emittedJoinPoints != null && genJp) {
341 for (int i = 0; i < out.emittedJoinPoints.length; i++) {
342 EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
343
344 String jpClassNoPackage = emittedJoinPoint.getJoinPointClassName();
345 if (jpClassNoPackage.indexOf('/')>0) {
346 jpClassNoPackage = jpClassNoPackage.substring(jpClassNoPackage.lastIndexOf('/'));
347 }
348 File jpFile = new File(file.getParent(), jpClassNoPackage+".class");
349 utility.log(" [genjp] " + jpFile.getCanonicalPath());
350 FileOutputStream jpFos = new FileOutputStream(jpFile);
351 JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(emittedJoinPoint, compilationLoader);
352 jpFos.write(compiledJp.bytecode);
353 jpFos.close();
354
355
356 CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
357 if (compiledCflowAspects.length > 0) {
358 String baseDirAbsolutePath = getBaseDir(file.getCanonicalPath(), className);
359 for (int j = 0; j < compiledCflowAspects.length; j++) {
360 CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
361 File cflowFile = new File(baseDirAbsolutePath + File.separatorChar + compiledCflowAspect.className.replace('/', File.separatorChar) + ".class");
362 (new File(cflowFile.getParent())).mkdirs();
363 utility.log(" [genjp] (cflow) " + cflowFile.getCanonicalPath());
364 FileOutputStream cflowFos = new FileOutputStream(cflowFile);
365 cflowFos.write(compiledCflowAspect.bytecode);
366 cflowFos.close();
367 }
368 }
369 }
370 }
371
372
373 if (verify) {
374 URLClassLoader verifier = new VerifierClassLoader(
375 compilationLoader.getURLs(),
376 ClassLoader.getSystemClassLoader()
377 );
378 try {
379 utility.log(" [verify] " + className);
380 Class.forName(className, false, verifier);
381 } catch (Throwable t) {
382 utility.log(" [verify] corrupted class: " + className);
383 throw new CompileException("corrupted class: " + className, t);
384 }
385 }
386 } catch (IOException e) {
387 throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
388 } finally {
389 try {
390 in.close();
391 } catch (Throwable e) {
392 ;
393 }
394 try {
395 fos.close();
396 } catch (Throwable e) {
397 ;
398 }
399 }
400 }
401
402 /***
403 * Compile all .class encountered in the .jar/.zip file. <p/>The target.jar is compiled in the
404 * target.jar.aspectwerkzc and the target.jar.aspectwerkzc then overrides target.jar on success.
405 */
406 public void compileJar(File file) throws CompileException {
407 utility.log(" [compilejar] " + file.getAbsolutePath());
408
409
410 File workingFile = new File(file.getAbsolutePath() + ".aspectwerkzc");
411 if (workingFile.exists()) {
412 workingFile.delete();
413 }
414 ZipFile zip = null;
415 ZipOutputStream zos = null;
416 try {
417 zip = new ZipFile(file);
418 zos = new ZipOutputStream(new FileOutputStream(workingFile));
419 for (Enumeration e = zip.entries(); e.hasMoreElements();) {
420 ZipEntry ze = (ZipEntry) e.nextElement();
421
422
423 InputStream in = zip.getInputStream(ze);
424 ByteArrayOutputStream bos = new ByteArrayOutputStream();
425 byte[] buffer = new byte[1024];
426 while (in.available() > 0) {
427 int length = in.read(buffer);
428 if (length == -1) {
429 break;
430 }
431 bos.write(buffer, 0, length);
432 }
433 in.close();
434
435
436 AspectWerkzPreProcessor.Output out = null;
437 byte[] transformed = null;
438 if (ze.getName().toLowerCase().endsWith(".class")) {
439 utility.log(" [compilejar] compile " + file.getName() + ":" + ze.getName());
440 String className = ze.getName().substring(0, ze.getName().length() - 6);
441 try {
442 out = preProcess(preprocessor, className, bos.toByteArray(), compilationLoader);
443 transformed = out.bytecode;
444 } catch (Throwable t) {
445 throw new CompileException("weaver failed for class: " + className, t);
446 }
447 } else {
448 out = null;
449 transformed = bos.toByteArray();
450 }
451
452
453 if (ze.getName().toLowerCase().equals("meta-inf/manifest.mf")) {
454 try {
455 Manifest mf = new Manifest(new ByteArrayInputStream(transformed));
456 Attributes at = mf.getMainAttributes();
457 at.putValue(MF_CUSTOM_DATE, DF.format(new Date()));
458 at.putValue(MF_CUSTOM_PP, preprocessor.getClass().getName());
459 at.putValue(MF_CUSTOM_COMMENT, MF_CUSTOM_COMMENT_VALUE);
460
461
462 bos.reset();
463 mf.write(bos);
464 transformed = bos.toByteArray();
465 } catch (Exception emf) {
466 emf.printStackTrace();
467 }
468 }
469
470
471 ZipEntry transformedZe = new ZipEntry(ze.getName());
472 transformedZe.setSize(transformed.length);
473 CRC32 crc = new CRC32();
474 crc.update(transformed);
475 transformedZe.setCrc(crc.getValue());
476 transformedZe.setMethod(ze.getMethod());
477 zos.putNextEntry(transformedZe);
478 zos.write(transformed, 0, transformed.length);
479
480
481 if (genJp && out != null && out.emittedJoinPoints!=null) {
482 for (int i = 0; i < out.emittedJoinPoints.length; i++) {
483 EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
484 JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(emittedJoinPoint, compilationLoader);
485 utility.log(" [compilejar] (genjp) " + file.getName() + ":" + emittedJoinPoint.getJoinPointClassName());
486 ZipEntry jpZe = new ZipEntry(emittedJoinPoint.getJoinPointClassName()+".class");
487 jpZe.setSize(compiledJp.bytecode.length);
488 CRC32 jpCrc = new CRC32();
489 jpCrc.update(compiledJp.bytecode);
490 jpZe.setCrc(jpCrc.getValue());
491 jpZe.setMethod(ze.getMethod());
492 zos.putNextEntry(jpZe);
493 zos.write(compiledJp.bytecode, 0, compiledJp.bytecode.length);
494
495 CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
496 if (compiledCflowAspects.length > 0) {
497 for (int j = 0; j < compiledCflowAspects.length; j++) {
498 CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
499 utility.log(" [compilejar] (genjp) (cflow) " + file.getName() + ":" + compiledCflowAspect.className);
500 ZipEntry cflowZe = new ZipEntry(compiledCflowAspect.className+".class");
501 cflowZe.setSize(compiledCflowAspect.bytecode.length);
502 CRC32 cflowCrc = new CRC32();
503 cflowCrc.update(compiledCflowAspect.bytecode);
504 cflowZe.setCrc(cflowCrc.getValue());
505 cflowZe.setMethod(ze.getMethod());
506 zos.putNextEntry(cflowZe);
507 zos.write(compiledCflowAspect.bytecode, 0, compiledCflowAspect.bytecode.length);
508 }
509 }
510 }
511 }
512 }
513 zip.close();
514 zos.close();
515
516
517 File swap = new File(file.getAbsolutePath() + ".swap.aspectwerkzc");
518 utility.backupFile(file, swap);
519 try {
520 utility.backupFile(workingFile, new File(file.getAbsolutePath()));
521 workingFile.delete();
522 swap.delete();
523 } catch (Exception e) {
524
525 utility.backupFile(swap, new File(file.getAbsolutePath()));
526 workingFile.delete();
527 throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
528 }
529 } catch (IOException e) {
530 throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
531 } finally {
532 try {
533 zos.close();
534 } catch (Throwable e) {
535 ;
536 }
537 try {
538 zip.close();
539 } catch (Throwable e) {
540 ;
541 }
542 }
543 }
544
545 /***
546 * Compile given target.
547 *
548 * @return false if process should stop
549 */
550 public boolean compile(File source) {
551 sourceIndex++;
552 backup(source, sourceIndex);
553 try {
554 doCompile(source, null);
555 } catch (CompileException e) {
556 utility.log(" [aspectwerkzc] compilation encountered an error");
557 e.printStackTrace();
558 return (!haltOnError);
559 }
560
561
562 successMap.put(source, Boolean.TRUE);
563 return true;
564 }
565
566 /***
567 * Set up the compilation path by building a URLClassLoader with all targets in
568 *
569 * @param targets to add to compilationLoader classpath
570 * @param parentLoader the parent ClassLoader used by the new one
571 */
572 public void setCompilationPath(File[] targets, ClassLoader parentLoader) {
573 URL[] urls = new URL[targets.length];
574 int j = 0;
575 for (int i = 0; i < targets.length; i++) {
576 try {
577 urls[j] = targets[i].getCanonicalFile().toURL();
578 j++;
579 } catch (IOException e) {
580 System.err.println("bad target " + targets[i]);
581 }
582 }
583
584 compilationLoader = new URLClassLoader(urls, parentLoader);
585 }
586
587 /***
588 * Test if file is a zip/jar file
589 */
590 public static boolean isJarFile(File source) {
591 return (source.isFile() && (source.getName().toLowerCase().endsWith(".jar") || source
592 .getName().toLowerCase().endsWith(".zip")));
593 }
594
595 /***
596 * Usage message
597 */
598 public static void doHelp() {
599 System.out.println("--- AspectWerkzC compiler ---");
600 System.out.println("Usage:");
601 System.out
602 .println(
603 "java -cp ... org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] <target 1> .. <target n>"
604 );
605 System.out.println(" <target i> : exploded dir, jar, zip files to compile");
606 }
607
608 /***
609 * Creates and configures an AspectWerkzC compiler.
610 *
611 * @param params a map containing the compiler parameters
612 * @return a new and configured <CODE>AspectWerkzC</CODE>
613 */
614 private static AspectWerkzC createCompiler(Map params) {
615 AspectWerkzC compiler = new AspectWerkzC();
616
617 for (Iterator it = params.entrySet().iterator(); it.hasNext();) {
618 Map.Entry param = (Map.Entry) it.next();
619
620 if (COMMAND_LINE_OPTION_VERBOSE.equals(param.getKey())) {
621 compiler.setVerbose(Boolean.TRUE.equals(param.getValue()));
622 } else if (COMMAND_LINE_OPTION_HALT.equals(param.getKey())) {
623 compiler.setHaltOnError(Boolean.TRUE.equals(param.getValue()));
624 } else if (COMMAND_LINE_OPTION_VERIFY.equals(param.getKey())) {
625 compiler.setVerify(Boolean.TRUE.equals(param.getValue()));
626 } else if (COMMAND_LINE_OPTION_GENJP.equals(param.getKey())) {
627 compiler.setGenJp(Boolean.TRUE.equals(param.getValue()));
628 } else if (COMMAND_LINE_OPTION_DETAILS.equals(param.getKey())) {
629 compiler.setDetails(Boolean.TRUE.equals(param.getValue()));
630 }
631 }
632
633 return compiler;
634 }
635
636 /***
637 * Runs the AspectWerkzC compiler for the <tt>targets</tt> files.
638 *
639 * @param compiler a configured <CODE>AspectWerkzC</CODE>
640 * @param classLoader the class loader to be used
641 * @param preProcessor fully qualified name of the preprocessor class.
642 * If <tt>null</tt> than the default is used
643 * (<CODE>org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor</CODE>)
644 * @param classpath list of Files representing the classpath (List<File>)
645 * @param targets the list of target files (List<File>)
646 */
647 public static void compile(AspectWerkzC compiler,
648 ClassLoader classLoader,
649 String preProcessor,
650 List classpath,
651 List targets) {
652 List fullPath = new ArrayList();
653 if (classpath != null) {
654 fullPath.addAll(classpath);
655 }
656
657 fullPath.addAll(targets);
658
659 compiler.setCompilationPath((File[]) fullPath.toArray(new File[fullPath.size()]), classLoader);
660
661 Thread.currentThread().setContextClassLoader(compiler.compilationLoader);
662
663
664
665
666 SystemDefinitionContainer.disableSystemWideDefinition();
667 SystemDefinitionContainer.deployDefinitions(
668 compiler.compilationLoader,
669 DefinitionLoader.getDefaultDefinition(compiler.compilationLoader)
670 );
671
672 String preprocessorFqn = preProcessor == null ? PRE_PROCESSOR_CLASSNAME_DEFAULT
673 : preProcessor;
674
675 try {
676 compiler.setPreprocessor(preprocessorFqn);
677 } catch (CompileException e) {
678 System.err.println("Cannot instantiate ClassPreProcessor: " + preprocessorFqn);
679 e.printStackTrace();
680 System.exit(-1);
681 }
682
683 cleanBackupDir(compiler);
684
685 for (Iterator i = targets.iterator(); i.hasNext();) {
686 if (!compiler.compile((File) i.next())) {
687 compiler.postCompile("*** An error occured ***");
688 System.exit(-1);
689 }
690 }
691 compiler.postCompile("");
692 }
693
694 private static void cleanBackupDir(AspectWerkzC compiler) {
695
696 try {
697 File temp = new File(compiler.backupDir);
698 if (temp.exists()) {
699 compiler.getUtility().deleteDir(temp);
700 }
701 temp.mkdir();
702 (new File(temp, "" + System.currentTimeMillis() + ".timestamp")).createNewFile();
703 } catch (Exception e) {
704 System.err.println("failed to prepare backup dir: " + compiler.backupDir);
705 e.printStackTrace();
706 System.exit(-1);
707 }
708 }
709
710 public static void main(String[] args) {
711 if (args.length <= 0) {
712 doHelp();
713 return;
714 }
715
716 Map options = parseOptions(args);
717 AspectWerkzC compiler = createCompiler(options);
718
719 compiler.setBackupDir(BACKUP_DIR);
720
721 compile(
722 compiler,
723 ClassLoader.getSystemClassLoader(),
724 System.getProperty(
725 PRE_PROCESSOR_CLASSNAME_PROPERTY,
726 PRE_PROCESSOR_CLASSNAME_DEFAULT
727 ),
728 (List) options.get(COMMAND_LINE_OPTION_CLASSPATH),
729 (List) options.get(COMMAND_LINE_OPTION_TARGETS)
730 );
731 }
732
733 private static Map parseOptions(String[] args) {
734 Map options = new HashMap();
735 List targets = new ArrayList();
736
737 for (int i = 0; i < args.length; i++) {
738 if (COMMAND_LINE_OPTION_VERBOSE.equals(args[i])) {
739 options.put(COMMAND_LINE_OPTION_VERBOSE, Boolean.TRUE);
740 } else if (COMMAND_LINE_OPTION_GENJP.equals(args[i])) {
741 options.put(COMMAND_LINE_OPTION_GENJP, Boolean.TRUE);
742 } else if (COMMAND_LINE_OPTION_DETAILS.equals(args[i])) {
743 options.put(COMMAND_LINE_OPTION_DETAILS, Boolean.TRUE);
744 } else if (COMMAND_LINE_OPTION_HALT.equals(args[i])) {
745 options.put(COMMAND_LINE_OPTION_HALT, Boolean.TRUE);
746 } else if (COMMAND_LINE_OPTION_VERIFY.equals(args[i])) {
747 options.put(COMMAND_LINE_OPTION_VERIFY, Boolean.TRUE);
748 } else if (COMMAND_LINE_OPTION_CLASSPATH.equals(args[i])) {
749 if (i == (args.length - 1)) {
750 continue;
751 } else {
752 options.put(
753 COMMAND_LINE_OPTION_CLASSPATH,
754 toFileArray(args[++i], File.pathSeparator)
755 );
756 }
757 } else if (args[i].startsWith(COMMAND_LINE_OPTION_DASH)) {
758 ;
759 } else {
760 File file = toFile(args[i]);
761 if (file == null) {
762 System.err.println("Ignoring inexistant target: " + args[i]);
763 } else {
764 targets.add(file);
765 }
766 }
767 }
768
769 options.put(COMMAND_LINE_OPTION_TARGETS, targets);
770
771 return options;
772 }
773
774 private static List toFileArray(String str, String sep) {
775 if (str == null || str.length() == 0) {
776 return new ArrayList();
777 }
778
779 List files = new ArrayList();
780 int start = 0;
781 int idx = str.indexOf(sep, start);
782 int len = sep.length();
783
784 while (idx != -1) {
785 files.add(new File(str.substring(start, idx)));
786 start = idx + len;
787 idx = str.indexOf(sep, start);
788 }
789
790 files.add(new File(str.substring(start)));
791
792 return files;
793 }
794
795 private static File toFile(String path) {
796 File file = new File(path);
797
798 return file.exists() ? file : null;
799 }
800
801 /***
802 * Helper method to have the emitted joinpoint back when dealing with AspectWerkz pp
803 * @param preProcessor
804 * @param className
805 * @param bytecode
806 * @param compilationLoader
807 * @return
808 */
809 private AspectWerkzPreProcessor.Output preProcess(ClassPreProcessor preProcessor, String className, byte[] bytecode, ClassLoader compilationLoader) {
810 if (isAspectWerkzPreProcessor) {
811 return ((AspectWerkzPreProcessor)preProcessor).preProcessWithOutput(className, bytecode, compilationLoader);
812 } else {
813 byte[] newBytes = preProcessor.preProcess(className, bytecode, compilationLoader);
814 AspectWerkzPreProcessor.Output out = new AspectWerkzPreProcessor.Output();
815 out.bytecode = newBytes;
816 return out;
817 }
818 }
819
820 /***
821 * Handles the compilation of the given emitted joinpoint
822 *
823 * @param emittedJoinPoint
824 * @param loader
825 * @return
826 * @throws IOException
827 */
828 private JoinPointManager.CompiledJoinPoint compileJoinPoint(EmittedJoinPoint emittedJoinPoint, ClassLoader loader) throws IOException {
829 try {
830 Class callerClass = ContextClassLoader.forName(emittedJoinPoint.getCallerClassName().replace('/', '.'));
831 Class calleeClass = ContextClassLoader.forName(emittedJoinPoint.getCalleeClassName().replace('/', '.'));
832 JoinPointManager.CompiledJoinPoint jp = JoinPointManager.compileJoinPoint(
833 emittedJoinPoint.getJoinPointType(),
834 callerClass,
835 emittedJoinPoint.getCallerMethodName(),
836 emittedJoinPoint.getCallerMethodDesc(),
837 emittedJoinPoint.getCallerMethodModifiers(),
838 emittedJoinPoint.getCalleeClassName(),
839 emittedJoinPoint.getCalleeMemberName(),
840 emittedJoinPoint.getCalleeMemberDesc(),
841 emittedJoinPoint.getCalleeMemberModifiers(),
842 emittedJoinPoint.getJoinPointHash(),
843 emittedJoinPoint.getJoinPointClassName(),
844 calleeClass,
845 loader
846 );
847 return jp;
848 } catch (ClassNotFoundException e) {
849 throw new IOException("Could not compile joinpoint : " + e.toString());
850 }
851 }
852
853 /***
854 * Handles the compilation of the possible cflowAspect associated to the advices that affects the given
855 * joinpoint
856 *
857 * @param jp
858 * @return
859 */
860 private CflowCompiler.CompiledCflowAspect[] compileCflows(JoinPointManager.CompiledJoinPoint jp) {
861 List allCflowBindings = new ArrayList();
862 AdviceInfoContainer adviceInfoContainer = jp.compilationInfo.getInitialModel().getAdviceInfoContainer();
863
864 AdviceInfo[] advices = adviceInfoContainer.getAllAdviceInfos();
865 for (int i = 0; i < advices.length; i++) {
866 AdviceInfo adviceInfo = advices[i];
867 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceInfo.getExpressionInfo());
868 allCflowBindings.addAll(cflowBindings);
869 }
870
871 List compiledCflows = new ArrayList();
872 for (Iterator iterator = allCflowBindings.iterator(); iterator.hasNext();) {
873 CflowBinding cflowBinding = (CflowBinding) iterator.next();
874 compiledCflows.add(CflowCompiler.compileCflowAspect(cflowBinding.getCflowID()));
875 }
876
877 return (CflowCompiler.CompiledCflowAspect[])compiledCflows.toArray(new CflowCompiler.CompiledCflowAspect[0]);
878 }
879
880 /***
881 * Given a path d/e/a/b/C.class and a class a.b.C, returns the base dir /d/e
882 *
883 * @param weavedClassFileFullPath
884 * @param weavedClassName
885 * @return
886 */
887 private static String getBaseDir(String weavedClassFileFullPath, String weavedClassName) {
888 String baseDirAbsolutePath = weavedClassFileFullPath;
889 int parentEndIndex = baseDirAbsolutePath.lastIndexOf(File.separatorChar);
890 for (int j = weavedClassName.toCharArray().length-1; j >= 0; j--) {
891 char c = weavedClassName.toCharArray()[j];
892 if (c == '.') {
893 if (parentEndIndex > 0) {
894 baseDirAbsolutePath = baseDirAbsolutePath.substring(0, parentEndIndex);
895 parentEndIndex = baseDirAbsolutePath.lastIndexOf(File.separatorChar);
896 }
897 }
898 }
899 if (parentEndIndex > 0) {
900 baseDirAbsolutePath = baseDirAbsolutePath.substring(0, parentEndIndex);
901 }
902 return baseDirAbsolutePath;
903 }
904 }