KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > compiler > AspectWerkzC


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 JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.URLClassLoader JavaDoc;
31 import java.text.SimpleDateFormat JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.Enumeration JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.Hashtable JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.jar.Attributes JavaDoc;
41 import java.util.jar.Manifest JavaDoc;
42 import java.util.zip.CRC32 JavaDoc;
43 import java.util.zip.ZipEntry JavaDoc;
44 import java.util.zip.ZipFile JavaDoc;
45 import java.util.zip.ZipOutputStream JavaDoc;
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 JavaDoc COMMAND_LINE_OPTION_DASH = "-";
89     private static final String JavaDoc COMMAND_LINE_OPTION_VERBOSE = "-verbose";
90     private static final String JavaDoc COMMAND_LINE_OPTION_DETAILS = "-details";
91     private static final String JavaDoc COMMAND_LINE_OPTION_GENJP = "-genjp";
92     private static final String JavaDoc COMMAND_LINE_OPTION_HALT = "-haltOnError";
93     private static final String JavaDoc COMMAND_LINE_OPTION_VERIFY = "-verify";
94     private static final String JavaDoc COMMAND_LINE_OPTION_CLASSPATH = "-cp";
95     private static final String JavaDoc COMMAND_LINE_OPTION_TARGETS = "compile.targets";
96
97     /**
98      * option used to defined the class preprocessor
99      */

100     private static final String JavaDoc PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
101
102     private final static String JavaDoc AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
103
104     /**
105      * default class preprocessor
106      */

107     private static final String JavaDoc PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
108
109     private final static String JavaDoc MF_CUSTOM_DATE = "X-AspectWerkzC-created";
110
111     private final static String JavaDoc MF_CUSTOM_PP = "X-AspectWerkzC-preprocessor";
112
113     private final static String JavaDoc MF_CUSTOM_COMMENT = "X-AspectWerkzC-comment";
114
115     private final static String JavaDoc MF_CUSTOM_COMMENT_VALUE = "AspectWerkzC - AspectWerkz compiler, aspectwerkz.codehaus.org";
116
117     private final static SimpleDateFormat JavaDoc DF = new SimpleDateFormat JavaDoc("yyyy-MM-dd HH:mm:ss");
118
119     private final static String JavaDoc 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 JavaDoc backupDir = BACKUP_DIR;
128
129     /**
130      * class loader in which the effective compilation occurs, child of system classloader
131      */

132     private URLClassLoader JavaDoc 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 JavaDoc backupMap = new HashMap JavaDoc();
149
150     /**
151      * Maps the target file to a status indicating compilation was successfull
152      */

153     private Map JavaDoc successMap = new HashMap JavaDoc();
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 JavaDoc 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 JavaDoc preprocessor) throws CompileException {
211         try {
212             Class JavaDoc 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 JavaDoc 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 JavaDoc source, int index) {
228         // backup source in BACKUP/index dir
229
File JavaDoc dest = new File JavaDoc(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 JavaDoc i = backupMap.keySet().iterator(); i.hasNext();) {
241             File JavaDoc source = (File JavaDoc) i.next();
242             if (!successMap.containsKey(source)) {
243                 File JavaDoc dest = (File JavaDoc) 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 JavaDoc message) {
253         restoreBackup();
254         utility.log(" [backup] removing backup");
255         utility.deleteDir(new File JavaDoc(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 JavaDoc i = backupMap.keySet().iterator(); i.hasNext();) {
260                 File JavaDoc source = (File JavaDoc) 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 JavaDoc sourceFile, String JavaDoc prefixPackage) throws CompileException {
278         if (sourceFile.isDirectory()) {
279             File JavaDoc[] 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 JavaDoc 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 JavaDoc file, String JavaDoc packaging) throws CompileException {
303         InputStream JavaDoc in = null;
304         FileOutputStream JavaDoc fos = null;
305         try {
306             utility.log(" [compile] " + file.getCanonicalPath());
307
308             // dump bytecode in byte[]
309
ByteArrayOutputStream JavaDoc bos = new ByteArrayOutputStream JavaDoc();
310             in = new FileInputStream JavaDoc(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 JavaDoc 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 JavaDoc t) {
331                 throw new CompileException("weaver failed for class: " + className, t);
332             }
333
334             // override file
335
fos = new FileOutputStream JavaDoc(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 JavaDoc jpClassNoPackage = emittedJoinPoint.getJoinPointClassName();
345                     if (jpClassNoPackage.indexOf('/')>0) {
346                         jpClassNoPackage = jpClassNoPackage.substring(jpClassNoPackage.lastIndexOf('/'));
347                     }
348                     File JavaDoc jpFile = new File JavaDoc(file.getParent(), jpClassNoPackage+".class");
349                     utility.log(" [genjp] " + jpFile.getCanonicalPath());
350                     FileOutputStream JavaDoc jpFos = new FileOutputStream JavaDoc(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 JavaDoc baseDirAbsolutePath = getBaseDir(file.getCanonicalPath(), className);
359                         for (int j = 0; j < compiledCflowAspects.length; j++) {
360                             CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
361                             File JavaDoc cflowFile = new File JavaDoc(baseDirAbsolutePath + File.separatorChar + compiledCflowAspect.className.replace('/', File.separatorChar) + ".class");
362                             (new File JavaDoc(cflowFile.getParent())).mkdirs();
363                             utility.log(" [genjp] (cflow) " + cflowFile.getCanonicalPath());
364                             FileOutputStream JavaDoc cflowFos = new FileOutputStream JavaDoc(cflowFile);
365                             cflowFos.write(compiledCflowAspect.bytecode);
366                             cflowFos.close();
367                         }
368                     }
369                 }
370             }
371
372             // verify modified class
373
if (verify) {
374                 URLClassLoader JavaDoc 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 JavaDoc t) {
382                     utility.log(" [verify] corrupted class: " + className);
383                     throw new CompileException("corrupted class: " + className, t);
384                 }
385             }
386         } catch (IOException JavaDoc e) {
387             throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
388         } finally {
389             try {
390                 in.close();
391             } catch (Throwable JavaDoc e) {
392                 ;
393             }
394             try {
395                 fos.close();
396             } catch (Throwable JavaDoc 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 JavaDoc file) throws CompileException {
407         utility.log(" [compilejar] " + file.getAbsolutePath());
408
409         // create an empty jar target.jar.aspectwerkzc
410
File JavaDoc workingFile = new File JavaDoc(file.getAbsolutePath() + ".aspectwerkzc");
411         if (workingFile.exists()) {
412             workingFile.delete();
413         }
414         ZipFile JavaDoc zip = null;
415         ZipOutputStream JavaDoc zos = null;
416         try {
417             zip = new ZipFile JavaDoc(file);
418             zos = new ZipOutputStream JavaDoc(new FileOutputStream JavaDoc(workingFile));
419             for (Enumeration JavaDoc e = zip.entries(); e.hasMoreElements();) {
420                 ZipEntry JavaDoc ze = (ZipEntry JavaDoc) e.nextElement();
421
422                 // dump bytes read in byte[]
423
InputStream JavaDoc in = zip.getInputStream(ze);
424                 ByteArrayOutputStream JavaDoc bos = new ByteArrayOutputStream JavaDoc();
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 JavaDoc 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 JavaDoc 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 JavaDoc mf = new Manifest JavaDoc(new ByteArrayInputStream JavaDoc(transformed));
456                         Attributes JavaDoc at = mf.getMainAttributes();
457                         at.putValue(MF_CUSTOM_DATE, DF.format(new Date JavaDoc()));
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 JavaDoc emf) {
466                         emf.printStackTrace();
467                     }
468                 }
469
470                 // update target.jar.aspectwerkzc working file
471
ZipEntry JavaDoc transformedZe = new ZipEntry JavaDoc(ze.getName());
472                 transformedZe.setSize(transformed.length);
473                 CRC32 JavaDoc crc = new CRC32 JavaDoc();
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 JavaDoc jpZe = new ZipEntry JavaDoc(emittedJoinPoint.getJoinPointClassName()+".class");
487                         jpZe.setSize(compiledJp.bytecode.length);
488                         CRC32 JavaDoc jpCrc = new CRC32 JavaDoc();
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 JavaDoc cflowZe = new ZipEntry JavaDoc(compiledCflowAspect.className+".class");
501                                 cflowZe.setSize(compiledCflowAspect.bytecode.length);
502                                 CRC32 JavaDoc cflowCrc = new CRC32 JavaDoc();
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 JavaDoc swap = new File JavaDoc(file.getAbsolutePath() + ".swap.aspectwerkzc");
518             utility.backupFile(file, swap);
519             try {
520                 utility.backupFile(workingFile, new File JavaDoc(file.getAbsolutePath()));
521                 workingFile.delete();
522                 swap.delete();
523             } catch (Exception JavaDoc e) {
524                 // restore swapFile
525
utility.backupFile(swap, new File JavaDoc(file.getAbsolutePath()));
526                 workingFile.delete();
527                 throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
528             }
529         } catch (IOException JavaDoc e) {
530             throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
531         } finally {
532             try {
533                 zos.close();
534             } catch (Throwable JavaDoc e) {
535                 ;
536             }
537             try {
538                 zip.close();
539             } catch (Throwable JavaDoc 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 JavaDoc 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 JavaDoc[] targets, ClassLoader JavaDoc parentLoader) {
573         URL JavaDoc[] urls = new URL JavaDoc[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 JavaDoc e) {
580                 System.err.println("bad target " + targets[i]);
581             }
582         }
583
584         compilationLoader = new URLClassLoader JavaDoc(urls, parentLoader);
585     }
586
587     /**
588      * Test if file is a zip/jar file
589      */

590     public static boolean isJarFile(File JavaDoc 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 JavaDoc params) {
615         AspectWerkzC compiler = new AspectWerkzC();
616
617         for (Iterator JavaDoc it = params.entrySet().iterator(); it.hasNext();) {
618             Map.Entry JavaDoc param = (Map.Entry JavaDoc) 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 JavaDoc classLoader,
649                                String JavaDoc preProcessor,
650                                List JavaDoc classpath,
651                                List JavaDoc targets) {
652         List JavaDoc fullPath = new ArrayList JavaDoc();
653         if (classpath != null) {
654             fullPath.addAll(classpath);
655         }
656
657         fullPath.addAll(targets);
658
659         compiler.setCompilationPath((File JavaDoc[]) fullPath.toArray(new File JavaDoc[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 JavaDoc 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 JavaDoc i = targets.iterator(); i.hasNext();) {
686             if (!compiler.compile((File JavaDoc) 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 JavaDoc temp = new File JavaDoc(compiler.backupDir);
698             if (temp.exists()) {
699                 compiler.getUtility().deleteDir(temp);
700             }
701             temp.mkdir();
702             (new File JavaDoc(temp, "" + System.currentTimeMillis() + ".timestamp")).createNewFile();
703         } catch (Exception JavaDoc 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 JavaDoc[] args) {
711         if (args.length <= 0) {
712             doHelp();
713             return; //stop here
714
}
715
716         Map JavaDoc 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 JavaDoc) options.get(COMMAND_LINE_OPTION_CLASSPATH),
729                 (List JavaDoc) options.get(COMMAND_LINE_OPTION_TARGETS)
730         );
731     }
732
733     private static Map JavaDoc parseOptions(String JavaDoc[] args) {
734         Map JavaDoc options = new HashMap JavaDoc();
735         List JavaDoc targets = new ArrayList JavaDoc();
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 JavaDoc 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 JavaDoc toFileArray(String JavaDoc str, String JavaDoc sep) {
775         if (str == null || str.length() == 0) {
776             return new ArrayList JavaDoc();
777         }
778
779         List JavaDoc files = new ArrayList JavaDoc();
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 JavaDoc(str.substring(start, idx)));
786             start = idx + len;
787             idx = str.indexOf(sep, start);
788         }
789
790         files.add(new File JavaDoc(str.substring(start)));
791
792         return files;
793     }
794
795     private static File JavaDoc toFile(String JavaDoc path) {
796         File JavaDoc file = new File JavaDoc(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 JavaDoc className, byte[] bytecode, ClassLoader JavaDoc 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 JavaDoc loader) throws IOException JavaDoc {
829         try {
830             Class JavaDoc callerClass = ContextClassLoader.forName(emittedJoinPoint.getCallerClassName().replace('/', '.'));
831             Class JavaDoc 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 JavaDoc e) {
849             throw new IOException JavaDoc("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 JavaDoc allCflowBindings = new ArrayList JavaDoc();
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 JavaDoc cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceInfo.getExpressionInfo());
868             allCflowBindings.addAll(cflowBindings);
869         }
870
871         List JavaDoc compiledCflows = new ArrayList JavaDoc();
872         for (Iterator JavaDoc 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 JavaDoc getBaseDir(String JavaDoc weavedClassFileFullPath, String JavaDoc weavedClassName) {
888         String JavaDoc 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 }
Popular Tags