View Javadoc

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      // COMMAND LINE OPTIONS
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         //@todo check for multiple transformation in compiler or in preprocessor ?
167         sourceIndex = 0;
168         utility = new Utility();
169         timer = System.currentTimeMillis();
170     }
171 
172     /*
173      * public void log(String msg) { utility.log(msg); } public void log(String msg, Throwable t) { utility.log(msg);
174      * t.printStackTrace(); }
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         // backup source in BACKUP/index dir
229         File dest = new File(this.backupDir + File.separator + index + File.separator + source.getName());
230         utility.backupFile(source, dest);
231 
232         // add to backupMap in case of rollback
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                     //@todo: jar encountered in a dir - use case ??
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             // dump bytecode in byte[]
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             // rebuild className
321             String className = file.getName().substring(0, file.getName().length() - 6);
322             if (packaging != null) {
323                 className = packaging + '.' + className;
324             }
325 
326             // transform
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             // override file
335             fos = new FileOutputStream(file);
336             fos.write(out.bytecode);
337             fos.close();
338 
339             // if AW and genjp
340             if (out.emittedJoinPoints != null && genJp) {
341                 for (int i = 0; i < out.emittedJoinPoints.length; i++) {
342                     EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
343                     //TODO we assume same package here.. make more generic
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                     // handle cflow if any
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             // verify modified class
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         // create an empty jar target.jar.aspectwerkzc
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                 // dump bytes read in byte[]
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                 // transform only .class file
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                 // customize Manifest.mf
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                         // re read the updated manifest
462                         bos.reset();
463                         mf.write(bos);
464                         transformed = bos.toByteArray();
465                     } catch (Exception emf) {
466                         emf.printStackTrace();
467                     }
468                 }
469 
470                 // update target.jar.aspectwerkzc working file
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                 // if AW and genjp
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             // replace file by workingFile
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                 // restore swapFile
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         // compile sucessfull
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         // AOPC special fix
664         // turn off -Daspectwerkz.definition.file registration and register it at the
665         // compilationLoader level instead
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         // prepare backup directory
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; //stop here
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; //FIXME: this is an error
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                 ; // nothing to be done about it
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 }