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 }