0001: /**************************************************************************************
0002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
0003: * http://aspectwerkz.codehaus.org *
0004: * ---------------------------------------------------------------------------------- *
0005: * The software in this package is published under the terms of the LGPL license *
0006: * a copy of which has been included with this distribution in the license.txt file. *
0007: **************************************************************************************/package org.codehaus.aspectwerkz.compiler;
0008:
0009: import org.codehaus.aspectwerkz.definition.DefinitionLoader;
0010: import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
0011: import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
0012: import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
0013: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
0014: import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
0015: import org.codehaus.aspectwerkz.joinpoint.management.AdviceInfoContainer;
0016: import org.codehaus.aspectwerkz.util.ContextClassLoader;
0017: import org.codehaus.aspectwerkz.aspect.AdviceInfo;
0018: import org.codehaus.aspectwerkz.cflow.CflowBinding;
0019: import org.codehaus.aspectwerkz.cflow.CflowCompiler;
0020:
0021: import java.io.ByteArrayInputStream;
0022: import java.io.ByteArrayOutputStream;
0023: import java.io.File;
0024: import java.io.FileInputStream;
0025: import java.io.FileOutputStream;
0026: import java.io.IOException;
0027: import java.io.InputStream;
0028: import java.net.URL;
0029: import java.net.URLClassLoader;
0030: import java.text.SimpleDateFormat;
0031: import java.util.ArrayList;
0032: import java.util.Date;
0033: import java.util.Enumeration;
0034: import java.util.HashMap;
0035: import java.util.Hashtable;
0036: import java.util.Iterator;
0037: import java.util.List;
0038: import java.util.Map;
0039: import java.util.jar.Attributes;
0040: import java.util.jar.Manifest;
0041: import java.util.zip.CRC32;
0042: import java.util.zip.ZipEntry;
0043: import java.util.zip.ZipFile;
0044: import java.util.zip.ZipOutputStream;
0045:
0046: /**
0047: * AspectWerkzC allow for precompilation of class / jar / zip given a class preprocessor. <p/>
0048: * <h2>Usage</h2>
0049: * <p/>
0050: * <pre>
0051: * java [-Daspectwerkz.classloader.preprocessor={ClassPreProcessorImpl}] -cp [...]
0052: * org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] [-genjp] [-details] [-cp {additional cp i}]* {target
0053: * 1} .. {target n}
0054: * {ClassPreProcessorImpl} : full qualified name of the ClassPreProcessor implementation (must be in classpath)
0055: * defaults to org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor
0056: * {additional cp i} : additionnal classpath needed at compile time (eg: myaspect.jar)
0057: * use as many -cp options as needed
0058: * supports java classpath syntax for classpath separator: ; on windows, : on others
0059: * {target i} : exploded dir, jar, zip files to compile
0060: * Ant 1.5 must be in the classpath
0061: * </pre>
0062: * <p/>
0063: * <p>
0064: * <h2>Classpath note</h2>
0065: * At the beginning of the compilation, all {target i} are added to the classpath automatically. <br/>This is required
0066: * to support caller side advices. <p/>
0067: * <h2>Error handling</h2>
0068: * For each target i, a backup copy is written in ./_aspectwerkzc/i/target <br/>Transformation occurs on original target
0069: * class/dir/jar/zip file <br/>On failure, target backup is restored and stacktrace is given <br/><br/>If
0070: * <i>-haltOnError </i> was set, compilations ends and a <b>complete </b> rollback occurs on all targets, else a status
0071: * report is printed at the end of the compilation, indicating SUCCESS or ERROR for each given target. <br/>If
0072: * <i>-verify </i> was set, all compiled class are verified during the compilation and an error is generated if the
0073: * compiled class bytecode is corrupted. The error is then handled according to the <i>-haltOnError </i> option. <br/>
0074: * <p/>
0075: * <h2>Manifest.mf update</h2>
0076: * The Manifest.mf if present is updated wit the following:
0077: * <ul>
0078: * <li>AspectWerkzC-created: date of the compilation</li>
0079: * <li>AspectWerkzC-preprocessor: full qualified classname of the preprocessor used</li>
0080: * <li>AspectWerkzC-comment: comments</li>
0081: * </ul>
0082: *
0083: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
0084: */
0085: public class AspectWerkzC {
0086: // COMMAND LINE OPTIONS
0087: private static final String COMMAND_LINE_OPTION_DASH = "-";
0088: private static final String COMMAND_LINE_OPTION_VERBOSE = "-verbose";
0089: private static final String COMMAND_LINE_OPTION_DETAILS = "-details";
0090: private static final String COMMAND_LINE_OPTION_GENJP = "-genjp";
0091: private static final String COMMAND_LINE_OPTION_HALT = "-haltOnError";
0092: private static final String COMMAND_LINE_OPTION_VERIFY = "-verify";
0093: private static final String COMMAND_LINE_OPTION_CLASSPATH = "-cp";
0094: private static final String COMMAND_LINE_OPTION_TARGETS = "compile.targets";
0095:
0096: /**
0097: * option used to defined the class preprocessor
0098: */
0099: private static final String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
0100:
0101: private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
0102:
0103: /**
0104: * default class preprocessor
0105: */
0106: private static final String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
0107:
0108: private final static String MF_CUSTOM_DATE = "X-AspectWerkzC-created";
0109:
0110: private final static String MF_CUSTOM_PP = "X-AspectWerkzC-preprocessor";
0111:
0112: private final static String MF_CUSTOM_COMMENT = "X-AspectWerkzC-comment";
0113:
0114: private final static String MF_CUSTOM_COMMENT_VALUE = "AspectWerkzC - AspectWerkz compiler, aspectwerkz.codehaus.org";
0115:
0116: private final static SimpleDateFormat DF = new SimpleDateFormat(
0117: "yyyy-MM-dd HH:mm:ss");
0118:
0119: private final static String BACKUP_DIR = "_aspectwerkzc";
0120:
0121: private boolean verify = false;
0122:
0123: private boolean genJp = false;
0124:
0125: private boolean haltOnError = false;
0126:
0127: private String backupDir = BACKUP_DIR;
0128:
0129: /**
0130: * class loader in which the effective compilation occurs, child of system classloader
0131: */
0132: private URLClassLoader compilationLoader = null;
0133:
0134: /**
0135: * class preprocessor instance used to compile targets
0136: */
0137: private ClassPreProcessor preprocessor = null;
0138: private boolean isAspectWerkzPreProcessor = false;
0139:
0140: /**
0141: * index to keep track of {target i} backups
0142: */
0143: private int sourceIndex;
0144:
0145: /**
0146: * Maps the target file to the target backup file
0147: */
0148: private Map backupMap = new HashMap();
0149:
0150: /**
0151: * Maps the target file to a status indicating compilation was successfull
0152: */
0153: private Map successMap = new HashMap();
0154:
0155: private long timer;
0156:
0157: /**
0158: * Utility for file manipulation
0159: */
0160: private Utility utility;
0161:
0162: /**
0163: * Construct a new Utility, restore the index for backup
0164: */
0165: public AspectWerkzC() {
0166: //@todo check for multiple transformation in compiler or in preprocessor ?
0167: sourceIndex = 0;
0168: utility = new Utility();
0169: timer = System.currentTimeMillis();
0170: }
0171:
0172: /*
0173: * public void log(String msg) { utility.log(msg); } public void log(String msg, Throwable t) { utility.log(msg);
0174: * t.printStackTrace(); }
0175: */
0176: public void setVerbose(boolean verbose) {
0177: utility.setVerbose(verbose);
0178: }
0179:
0180: public void setGenJp(boolean genpJp) {
0181: this .genJp = genpJp;
0182: }
0183:
0184: public void setHaltOnError(boolean haltOnError) {
0185: this .haltOnError = haltOnError;
0186: }
0187:
0188: public void setVerify(boolean verify) {
0189: this .verify = verify;
0190: }
0191:
0192: public void setDetails(boolean details) {
0193: if (details) {
0194: System.setProperty(AW_TRANSFORM_DETAILS, "true");
0195: }
0196: }
0197:
0198: public void setBackupDir(String backup) {
0199: this .backupDir = backup;
0200: }
0201:
0202: public Utility getUtility() {
0203: return utility;
0204: }
0205:
0206: /**
0207: * Sets the ClassPreProcessor implementation to use. <p/>The ClassLoader will be set to System ClassLoader when
0208: * transform(className, byteCode, callerClassLoader) will be called to compile a class.
0209: */
0210: public void setPreprocessor(String preprocessor)
0211: throws CompileException {
0212: try {
0213: Class pp = Class.forName(preprocessor);
0214: this .preprocessor = (ClassPreProcessor) pp.newInstance();
0215: this .preprocessor.initialize();
0216:
0217: if (this .preprocessor instanceof AspectWerkzPreProcessor) {
0218: isAspectWerkzPreProcessor = true;
0219: }
0220: } catch (Exception e) {
0221: throw new CompileException(
0222: "failed to instantiate preprocessor "
0223: + preprocessor, e);
0224: }
0225: }
0226:
0227: /**
0228: * Backup source file in backup_dir/index/file. The backupMap is updated for further rollback
0229: */
0230: public void backup(File source, int index) {
0231: // backup source in BACKUP/index dir
0232: File dest = new File(this .backupDir + File.separator + index
0233: + File.separator + source.getName());
0234: utility.backupFile(source, dest);
0235:
0236: // add to backupMap in case of rollback
0237: backupMap.put(source, dest);
0238: }
0239:
0240: /**
0241: * Restore the backup registered
0242: */
0243: public void restoreBackup() {
0244: for (Iterator i = backupMap.keySet().iterator(); i.hasNext();) {
0245: File source = (File) i.next();
0246: if (!successMap.containsKey(source)) {
0247: File dest = (File) backupMap.get(source);
0248: utility.backupFile(dest, source);
0249: }
0250: }
0251: }
0252:
0253: /**
0254: * Delete backup dir at the end of all compilation
0255: */
0256: public void postCompile(String message) {
0257: restoreBackup();
0258: utility.log(" [backup] removing backup");
0259: utility.deleteDir(new File(this .backupDir));
0260: long ms = Math
0261: .max(System.currentTimeMillis() - timer, 1 * 1000);
0262: System.out
0263: .println("( " + (int) (ms / 1000) + " s ) " + message);
0264: if (!haltOnError) {
0265: for (Iterator i = backupMap.keySet().iterator(); i
0266: .hasNext();) {
0267: File source = (File) i.next();
0268: if (successMap.containsKey(source)) {
0269: System.out.println("SUCCESS: " + source);
0270: } else {
0271: System.out.println("FAILED : " + source);
0272: }
0273: }
0274: }
0275: }
0276:
0277: /**
0278: * Compile sourceFile. If prefixPackage is not null, assumes it is the class package information. <p/>Handles :
0279: * <ul>
0280: * <li>directory recursively (exploded jar)</li>
0281: * <li>jar / zip file</li>
0282: * </ul>
0283: */
0284: public void doCompile(File sourceFile, String prefixPackage)
0285: throws CompileException {
0286: if (sourceFile.isDirectory()) {
0287: File[] classes = sourceFile.listFiles();
0288: for (int i = 0; i < classes.length; i++) {
0289: if (classes[i].isDirectory()
0290: && !(this .backupDir
0291: .equals(classes[i].getName()))) {
0292: String packaging = (prefixPackage != null) ? (prefixPackage
0293: + "." + classes[i].getName())
0294: : classes[i].getName();
0295: doCompile(classes[i], packaging);
0296: } else if (classes[i].getName().toLowerCase().endsWith(
0297: ".class")) {
0298: compileClass(classes[i], prefixPackage);
0299: } else if (isJarFile(classes[i])) {
0300: //@todo: jar encountered in a dir - use case ??
0301: compileJar(classes[i]);
0302: }
0303: }
0304: } else if (sourceFile.getName().toLowerCase()
0305: .endsWith(".class")) {
0306: compileClass(sourceFile, null);
0307: } else if (isJarFile(sourceFile)) {
0308: compileJar(sourceFile);
0309: }
0310: }
0311:
0312: /**
0313: * Compiles .class file using fileName as className and given packaging as package name
0314: */
0315: public void compileClass(File file, String packaging)
0316: throws CompileException {
0317: InputStream in = null;
0318: FileOutputStream fos = null;
0319: try {
0320: utility.log(" [compile] " + file.getCanonicalPath());
0321:
0322: // dump bytecode in byte[]
0323: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0324: in = new FileInputStream(file);
0325: byte[] buffer = new byte[1024];
0326: while (in.available() > 0) {
0327: int length = in.read(buffer);
0328: if (length == -1) {
0329: break;
0330: }
0331: bos.write(buffer, 0, length);
0332: }
0333:
0334: // rebuild className
0335: String className = file.getName().substring(0,
0336: file.getName().length() - 6);
0337: if (packaging != null) {
0338: className = packaging + '.' + className;
0339: }
0340:
0341: // transform
0342: AspectWerkzPreProcessor.Output out = null;
0343: try {
0344: out = preProcess(preprocessor, className, bos
0345: .toByteArray(), compilationLoader);
0346: } catch (Throwable t) {
0347: throw new CompileException("weaver failed for class: "
0348: + className, t);
0349: }
0350:
0351: // override file
0352: fos = new FileOutputStream(file);
0353: fos.write(out.bytecode);
0354: fos.close();
0355:
0356: // if AW and genjp
0357: if (out.emittedJoinPoints != null && genJp) {
0358: for (int i = 0; i < out.emittedJoinPoints.length; i++) {
0359: EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
0360: //TODO we assume same package here.. make more generic
0361: String jpClassNoPackage = emittedJoinPoint
0362: .getJoinPointClassName();
0363: if (jpClassNoPackage.indexOf('/') > 0) {
0364: jpClassNoPackage = jpClassNoPackage
0365: .substring(jpClassNoPackage
0366: .lastIndexOf('/'));
0367: }
0368: File jpFile = new File(file.getParent(),
0369: jpClassNoPackage + ".class");
0370: utility
0371: .log(" [genjp] "
0372: + jpFile.getCanonicalPath());
0373: FileOutputStream jpFos = new FileOutputStream(
0374: jpFile);
0375: JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(
0376: emittedJoinPoint, compilationLoader);
0377: jpFos.write(compiledJp.bytecode);
0378: jpFos.close();
0379:
0380: // handle cflow if any
0381: CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
0382: if (compiledCflowAspects.length > 0) {
0383: String baseDirAbsolutePath = getBaseDir(file
0384: .getCanonicalPath(), className);
0385: for (int j = 0; j < compiledCflowAspects.length; j++) {
0386: CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
0387: File cflowFile = new File(
0388: baseDirAbsolutePath
0389: + File.separatorChar
0390: + compiledCflowAspect.className
0391: .replace(
0392: '/',
0393: File.separatorChar)
0394: + ".class");
0395: (new File(cflowFile.getParent())).mkdirs();
0396: utility.log(" [genjp] (cflow) "
0397: + cflowFile.getCanonicalPath());
0398: FileOutputStream cflowFos = new FileOutputStream(
0399: cflowFile);
0400: cflowFos
0401: .write(compiledCflowAspect.bytecode);
0402: cflowFos.close();
0403: }
0404: }
0405: }
0406: }
0407:
0408: // verify modified class
0409: if (verify) {
0410: URLClassLoader verifier = new VerifierClassLoader(
0411: compilationLoader.getURLs(), ClassLoader
0412: .getSystemClassLoader());
0413: try {
0414: utility.log(" [verify] " + className);
0415: Class.forName(className, false, verifier);
0416: } catch (Throwable t) {
0417: utility.log(" [verify] corrupted class: "
0418: + className);
0419: throw new CompileException("corrupted class: "
0420: + className, t);
0421: }
0422: }
0423: } catch (IOException e) {
0424: throw new CompileException("compile "
0425: + file.getAbsolutePath() + " failed", e);
0426: } finally {
0427: try {
0428: in.close();
0429: } catch (Throwable e) {
0430: ;
0431: }
0432: try {
0433: fos.close();
0434: } catch (Throwable e) {
0435: ;
0436: }
0437: }
0438: }
0439:
0440: /**
0441: * Compile all .class encountered in the .jar/.zip file. <p/>The target.jar is compiled in the
0442: * target.jar.aspectwerkzc and the target.jar.aspectwerkzc then overrides target.jar on success.
0443: */
0444: public void compileJar(File file) throws CompileException {
0445: utility.log(" [compilejar] " + file.getAbsolutePath());
0446:
0447: // create an empty jar target.jar.aspectwerkzc
0448: File workingFile = new File(file.getAbsolutePath()
0449: + ".aspectwerkzc");
0450: if (workingFile.exists()) {
0451: workingFile.delete();
0452: }
0453: ZipFile zip = null;
0454: ZipOutputStream zos = null;
0455: try {
0456: zip = new ZipFile(file);
0457: zos = new ZipOutputStream(new FileOutputStream(workingFile));
0458: for (Enumeration e = zip.entries(); e.hasMoreElements();) {
0459: ZipEntry ze = (ZipEntry) e.nextElement();
0460:
0461: // dump bytes read in byte[]
0462: InputStream in = zip.getInputStream(ze);
0463: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0464: byte[] buffer = new byte[1024];
0465: while (in.available() > 0) {
0466: int length = in.read(buffer);
0467: if (length == -1) {
0468: break;
0469: }
0470: bos.write(buffer, 0, length);
0471: }
0472: in.close();
0473:
0474: // transform only .class file
0475: AspectWerkzPreProcessor.Output out = null;
0476: byte[] transformed = null;
0477: if (ze.getName().toLowerCase().endsWith(".class")) {
0478: utility.log(" [compilejar] compile "
0479: + file.getName() + ":" + ze.getName());
0480: String className = ze.getName().substring(0,
0481: ze.getName().length() - 6);
0482: try {
0483: out = preProcess(preprocessor, className, bos
0484: .toByteArray(), compilationLoader);
0485: transformed = out.bytecode;
0486: } catch (Throwable t) {
0487: throw new CompileException(
0488: "weaver failed for class: " + className,
0489: t);
0490: }
0491: } else {
0492: out = null;
0493: transformed = bos.toByteArray();
0494: }
0495:
0496: // customize Manifest.mf
0497: if (ze.getName().toLowerCase().equals(
0498: "meta-inf/manifest.mf")) {
0499: try {
0500: Manifest mf = new Manifest(
0501: new ByteArrayInputStream(transformed));
0502: Attributes at = mf.getMainAttributes();
0503: at.putValue(MF_CUSTOM_DATE, DF
0504: .format(new Date()));
0505: at.putValue(MF_CUSTOM_PP, preprocessor
0506: .getClass().getName());
0507: at.putValue(MF_CUSTOM_COMMENT,
0508: MF_CUSTOM_COMMENT_VALUE);
0509:
0510: // re read the updated manifest
0511: bos.reset();
0512: mf.write(bos);
0513: transformed = bos.toByteArray();
0514: } catch (Exception emf) {
0515: emf.printStackTrace();
0516: }
0517: }
0518:
0519: // update target.jar.aspectwerkzc working file
0520: ZipEntry transformedZe = new ZipEntry(ze.getName());
0521: transformedZe.setSize(transformed.length);
0522: CRC32 crc = new CRC32();
0523: crc.update(transformed);
0524: transformedZe.setCrc(crc.getValue());
0525: transformedZe.setMethod(ze.getMethod());
0526: zos.putNextEntry(transformedZe);
0527: zos.write(transformed, 0, transformed.length);
0528:
0529: // if AW and genjp
0530: if (genJp && out != null
0531: && out.emittedJoinPoints != null) {
0532: for (int i = 0; i < out.emittedJoinPoints.length; i++) {
0533: EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
0534: JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(
0535: emittedJoinPoint, compilationLoader);
0536: utility.log(" [compilejar] (genjp) "
0537: + file.getName()
0538: + ":"
0539: + emittedJoinPoint
0540: .getJoinPointClassName());
0541: ZipEntry jpZe = new ZipEntry(emittedJoinPoint
0542: .getJoinPointClassName()
0543: + ".class");
0544: jpZe.setSize(compiledJp.bytecode.length);
0545: CRC32 jpCrc = new CRC32();
0546: jpCrc.update(compiledJp.bytecode);
0547: jpZe.setCrc(jpCrc.getValue());
0548: jpZe.setMethod(ze.getMethod());
0549: zos.putNextEntry(jpZe);
0550: zos.write(compiledJp.bytecode, 0,
0551: compiledJp.bytecode.length);
0552:
0553: CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
0554: if (compiledCflowAspects.length > 0) {
0555: for (int j = 0; j < compiledCflowAspects.length; j++) {
0556: CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
0557: utility
0558: .log(" [compilejar] (genjp) (cflow) "
0559: + file.getName()
0560: + ":"
0561: + compiledCflowAspect.className);
0562: ZipEntry cflowZe = new ZipEntry(
0563: compiledCflowAspect.className
0564: + ".class");
0565: cflowZe
0566: .setSize(compiledCflowAspect.bytecode.length);
0567: CRC32 cflowCrc = new CRC32();
0568: cflowCrc
0569: .update(compiledCflowAspect.bytecode);
0570: cflowZe.setCrc(cflowCrc.getValue());
0571: cflowZe.setMethod(ze.getMethod());
0572: zos.putNextEntry(cflowZe);
0573: zos
0574: .write(
0575: compiledCflowAspect.bytecode,
0576: 0,
0577: compiledCflowAspect.bytecode.length);
0578: }
0579: }
0580: }
0581: }
0582: }
0583: zip.close();
0584: zos.close();
0585:
0586: // replace file by workingFile
0587: File swap = new File(file.getAbsolutePath()
0588: + ".swap.aspectwerkzc");
0589: utility.backupFile(file, swap);
0590: try {
0591: utility.backupFile(workingFile, new File(file
0592: .getAbsolutePath()));
0593: workingFile.delete();
0594: swap.delete();
0595: } catch (Exception e) {
0596: // restore swapFile
0597: utility.backupFile(swap, new File(file
0598: .getAbsolutePath()));
0599: workingFile.delete();
0600: throw new CompileException("compile "
0601: + file.getAbsolutePath() + " failed", e);
0602: }
0603: } catch (IOException e) {
0604: throw new CompileException("compile "
0605: + file.getAbsolutePath() + " failed", e);
0606: } finally {
0607: try {
0608: zos.close();
0609: } catch (Throwable e) {
0610: ;
0611: }
0612: try {
0613: zip.close();
0614: } catch (Throwable e) {
0615: ;
0616: }
0617: }
0618: }
0619:
0620: /**
0621: * Compile given target.
0622: *
0623: * @return false if process should stop
0624: */
0625: public boolean compile(File source) {
0626: sourceIndex++;
0627: backup(source, sourceIndex);
0628: try {
0629: doCompile(source, null);
0630: } catch (CompileException e) {
0631: utility
0632: .log(" [aspectwerkzc] compilation encountered an error");
0633: e.printStackTrace();
0634: return (!haltOnError);
0635: }
0636:
0637: // compile sucessfull
0638: successMap.put(source, Boolean.TRUE);
0639: return true;
0640: }
0641:
0642: /**
0643: * Set up the compilation path by building a URLClassLoader with all targets in
0644: *
0645: * @param targets to add to compilationLoader classpath
0646: * @param parentLoader the parent ClassLoader used by the new one
0647: */
0648: public void setCompilationPath(File[] targets,
0649: ClassLoader parentLoader) {
0650: URL[] urls = new URL[targets.length];
0651: int j = 0;
0652: for (int i = 0; i < targets.length; i++) {
0653: try {
0654: urls[j] = targets[i].getCanonicalFile().toURL();
0655: j++;
0656: } catch (IOException e) {
0657: System.err.println("bad target " + targets[i]);
0658: }
0659: }
0660:
0661: compilationLoader = new URLClassLoader(urls, parentLoader);
0662: }
0663:
0664: /**
0665: * Test if file is a zip/jar file
0666: */
0667: public static boolean isJarFile(File source) {
0668: return (source.isFile() && (source.getName().toLowerCase()
0669: .endsWith(".jar") || source.getName().toLowerCase()
0670: .endsWith(".zip")));
0671: }
0672:
0673: /**
0674: * Usage message
0675: */
0676: public static void doHelp() {
0677: System.out.println("--- AspectWerkzC compiler ---");
0678: System.out.println("Usage:");
0679: System.out
0680: .println("java -cp ... org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] <target 1> .. <target n>");
0681: System.out
0682: .println(" <target i> : exploded dir, jar, zip files to compile");
0683: }
0684:
0685: /**
0686: * Creates and configures an AspectWerkzC compiler.
0687: *
0688: * @param params a map containing the compiler parameters
0689: * @return a new and configured <CODE>AspectWerkzC</CODE>
0690: */
0691: private static AspectWerkzC createCompiler(Map params) {
0692: AspectWerkzC compiler = new AspectWerkzC();
0693:
0694: for (Iterator it = params.entrySet().iterator(); it.hasNext();) {
0695: Map.Entry param = (Map.Entry) it.next();
0696:
0697: if (COMMAND_LINE_OPTION_VERBOSE.equals(param.getKey())) {
0698: compiler.setVerbose(Boolean.TRUE.equals(param
0699: .getValue()));
0700: } else if (COMMAND_LINE_OPTION_HALT.equals(param.getKey())) {
0701: compiler.setHaltOnError(Boolean.TRUE.equals(param
0702: .getValue()));
0703: } else if (COMMAND_LINE_OPTION_VERIFY
0704: .equals(param.getKey())) {
0705: compiler.setVerify(Boolean.TRUE
0706: .equals(param.getValue()));
0707: } else if (COMMAND_LINE_OPTION_GENJP.equals(param.getKey())) {
0708: compiler
0709: .setGenJp(Boolean.TRUE.equals(param.getValue()));
0710: } else if (COMMAND_LINE_OPTION_DETAILS.equals(param
0711: .getKey())) {
0712: compiler.setDetails(Boolean.TRUE.equals(param
0713: .getValue()));
0714: }
0715: }
0716:
0717: return compiler;
0718: }
0719:
0720: /**
0721: * Runs the AspectWerkzC compiler for the <tt>targets</tt> files.
0722: *
0723: * @param compiler a configured <CODE>AspectWerkzC</CODE>
0724: * @param classLoader the class loader to be used
0725: * @param preProcessor fully qualified name of the preprocessor class.
0726: * If <tt>null</tt> than the default is used
0727: * (<CODE>org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor</CODE>)
0728: * @param classpath list of Files representing the classpath (List<File>)
0729: * @param targets the list of target files (List<File>)
0730: */
0731: public static void compile(AspectWerkzC compiler,
0732: ClassLoader classLoader, String preProcessor,
0733: List classpath, List targets) {
0734: List fullPath = new ArrayList();
0735: if (classpath != null) {
0736: fullPath.addAll(classpath);
0737: }
0738:
0739: fullPath.addAll(targets);
0740:
0741: compiler.setCompilationPath((File[]) fullPath
0742: .toArray(new File[fullPath.size()]), classLoader);
0743:
0744: Thread.currentThread().setContextClassLoader(
0745: compiler.compilationLoader);
0746:
0747: // AOPC special fix
0748: // turn off -Daspectwerkz.definition.file registration and register it at the
0749: // compilationLoader level instead
0750: SystemDefinitionContainer.disableSystemWideDefinition();
0751: SystemDefinitionContainer
0752: .deployDefinitions(
0753: compiler.compilationLoader,
0754: DefinitionLoader
0755: .getDefaultDefinition(compiler.compilationLoader));
0756:
0757: String preprocessorFqn = preProcessor == null ? PRE_PROCESSOR_CLASSNAME_DEFAULT
0758: : preProcessor;
0759:
0760: try {
0761: compiler.setPreprocessor(preprocessorFqn);
0762: } catch (CompileException e) {
0763: System.err.println("Cannot instantiate ClassPreProcessor: "
0764: + preprocessorFqn);
0765: e.printStackTrace();
0766: System.exit(-1);
0767: }
0768:
0769: cleanBackupDir(compiler);
0770:
0771: for (Iterator i = targets.iterator(); i.hasNext();) {
0772: if (!compiler.compile((File) i.next())) {
0773: compiler.postCompile("*** An error occured ***");
0774: System.exit(-1);
0775: }
0776: }
0777: compiler.postCompile("");
0778: }
0779:
0780: private static void cleanBackupDir(AspectWerkzC compiler) {
0781: // prepare backup directory
0782: try {
0783: File temp = new File(compiler.backupDir);
0784: if (temp.exists()) {
0785: compiler.getUtility().deleteDir(temp);
0786: }
0787: temp.mkdir();
0788: (new File(temp, "" + System.currentTimeMillis()
0789: + ".timestamp")).createNewFile();
0790: } catch (Exception e) {
0791: System.err.println("failed to prepare backup dir: "
0792: + compiler.backupDir);
0793: e.printStackTrace();
0794: System.exit(-1);
0795: }
0796: }
0797:
0798: public static void main(String[] args) {
0799: if (args.length <= 0) {
0800: doHelp();
0801: return; //stop here
0802: }
0803:
0804: Map options = parseOptions(args);
0805: AspectWerkzC compiler = createCompiler(options);
0806:
0807: compiler.setBackupDir(BACKUP_DIR);
0808:
0809: compile(compiler, ClassLoader.getSystemClassLoader(), System
0810: .getProperty(PRE_PROCESSOR_CLASSNAME_PROPERTY,
0811: PRE_PROCESSOR_CLASSNAME_DEFAULT),
0812: (List) options.get(COMMAND_LINE_OPTION_CLASSPATH),
0813: (List) options.get(COMMAND_LINE_OPTION_TARGETS));
0814: }
0815:
0816: private static Map parseOptions(String[] args) {
0817: Map options = new HashMap();
0818: List targets = new ArrayList();
0819:
0820: for (int i = 0; i < args.length; i++) {
0821: if (COMMAND_LINE_OPTION_VERBOSE.equals(args[i])) {
0822: options.put(COMMAND_LINE_OPTION_VERBOSE, Boolean.TRUE);
0823: } else if (COMMAND_LINE_OPTION_GENJP.equals(args[i])) {
0824: options.put(COMMAND_LINE_OPTION_GENJP, Boolean.TRUE);
0825: } else if (COMMAND_LINE_OPTION_DETAILS.equals(args[i])) {
0826: options.put(COMMAND_LINE_OPTION_DETAILS, Boolean.TRUE);
0827: } else if (COMMAND_LINE_OPTION_HALT.equals(args[i])) {
0828: options.put(COMMAND_LINE_OPTION_HALT, Boolean.TRUE);
0829: } else if (COMMAND_LINE_OPTION_VERIFY.equals(args[i])) {
0830: options.put(COMMAND_LINE_OPTION_VERIFY, Boolean.TRUE);
0831: } else if (COMMAND_LINE_OPTION_CLASSPATH.equals(args[i])) {
0832: if (i == (args.length - 1)) {
0833: continue; //FIXME: this is an error
0834: } else {
0835: options.put(COMMAND_LINE_OPTION_CLASSPATH,
0836: toFileArray(args[++i], File.pathSeparator));
0837: }
0838: } else if (args[i].startsWith(COMMAND_LINE_OPTION_DASH)) {
0839: ; // nothing to be done about it
0840: } else {
0841: File file = toFile(args[i]);
0842: if (file == null) {
0843: System.err.println("Ignoring inexistant target: "
0844: + args[i]);
0845: } else {
0846: targets.add(file);
0847: }
0848: }
0849: }
0850:
0851: options.put(COMMAND_LINE_OPTION_TARGETS, targets);
0852:
0853: return options;
0854: }
0855:
0856: private static List toFileArray(String str, String sep) {
0857: if (str == null || str.length() == 0) {
0858: return new ArrayList();
0859: }
0860:
0861: List files = new ArrayList();
0862: int start = 0;
0863: int idx = str.indexOf(sep, start);
0864: int len = sep.length();
0865:
0866: while (idx != -1) {
0867: files.add(new File(str.substring(start, idx)));
0868: start = idx + len;
0869: idx = str.indexOf(sep, start);
0870: }
0871:
0872: files.add(new File(str.substring(start)));
0873:
0874: return files;
0875: }
0876:
0877: private static File toFile(String path) {
0878: File file = new File(path);
0879:
0880: return file.exists() ? file : null;
0881: }
0882:
0883: /**
0884: * Helper method to have the emitted joinpoint back when dealing with AspectWerkz pp
0885: * @param preProcessor
0886: * @param className
0887: * @param bytecode
0888: * @param compilationLoader
0889: * @return
0890: */
0891: private AspectWerkzPreProcessor.Output preProcess(
0892: ClassPreProcessor preProcessor, String className,
0893: byte[] bytecode, ClassLoader compilationLoader) {
0894: if (isAspectWerkzPreProcessor) {
0895: return ((AspectWerkzPreProcessor) preProcessor)
0896: .preProcessWithOutput(className, bytecode,
0897: compilationLoader);
0898: } else {
0899: byte[] newBytes = preProcessor.preProcess(className,
0900: bytecode, compilationLoader);
0901: AspectWerkzPreProcessor.Output out = new AspectWerkzPreProcessor.Output();
0902: out.bytecode = newBytes;
0903: return out;
0904: }
0905: }
0906:
0907: /**
0908: * Handles the compilation of the given emitted joinpoint
0909: *
0910: * @param emittedJoinPoint
0911: * @param loader
0912: * @return
0913: * @throws IOException
0914: */
0915: private JoinPointManager.CompiledJoinPoint compileJoinPoint(
0916: EmittedJoinPoint emittedJoinPoint, ClassLoader loader)
0917: throws IOException {
0918: try {
0919: Class callerClass = ContextClassLoader
0920: .forName(emittedJoinPoint.getCallerClassName()
0921: .replace('/', '.'));
0922: Class calleeClass = ContextClassLoader
0923: .forName(emittedJoinPoint.getCalleeClassName()
0924: .replace('/', '.'));
0925: JoinPointManager.CompiledJoinPoint jp = JoinPointManager
0926: .compileJoinPoint(
0927: emittedJoinPoint.getJoinPointType(),
0928: callerClass,
0929: emittedJoinPoint.getCallerMethodName(),
0930: emittedJoinPoint.getCallerMethodDesc(),
0931: emittedJoinPoint.getCallerMethodModifiers(),
0932: emittedJoinPoint.getCalleeClassName(),
0933: emittedJoinPoint.getCalleeMemberName(),
0934: emittedJoinPoint.getCalleeMemberDesc(),
0935: emittedJoinPoint.getCalleeMemberModifiers(),
0936: emittedJoinPoint.getJoinPointHash(),
0937: emittedJoinPoint.getJoinPointClassName(),
0938: calleeClass, loader);
0939: return jp;
0940: } catch (ClassNotFoundException e) {
0941: throw new IOException("Could not compile joinpoint : "
0942: + e.toString());
0943: }
0944: }
0945:
0946: /**
0947: * Handles the compilation of the possible cflowAspect associated to the advices that affects the given
0948: * joinpoint
0949: *
0950: * @param jp
0951: * @return
0952: */
0953: private CflowCompiler.CompiledCflowAspect[] compileCflows(
0954: JoinPointManager.CompiledJoinPoint jp) {
0955: List allCflowBindings = new ArrayList();
0956: AdviceInfoContainer adviceInfoContainer = jp.compilationInfo
0957: .getInitialModel().getAdviceInfoContainer();
0958:
0959: AdviceInfo[] advices = adviceInfoContainer.getAllAdviceInfos();
0960: for (int i = 0; i < advices.length; i++) {
0961: AdviceInfo adviceInfo = advices[i];
0962: List cflowBindings = CflowBinding
0963: .getCflowBindingsForCflowOf(adviceInfo
0964: .getExpressionInfo());
0965: allCflowBindings.addAll(cflowBindings);
0966: }
0967:
0968: List compiledCflows = new ArrayList();
0969: for (Iterator iterator = allCflowBindings.iterator(); iterator
0970: .hasNext();) {
0971: CflowBinding cflowBinding = (CflowBinding) iterator.next();
0972: compiledCflows.add(CflowCompiler
0973: .compileCflowAspect(cflowBinding.getCflowID()));
0974: }
0975:
0976: return (CflowCompiler.CompiledCflowAspect[]) compiledCflows
0977: .toArray(new CflowCompiler.CompiledCflowAspect[0]);
0978: }
0979:
0980: /**
0981: * Given a path d/e/a/b/C.class and a class a.b.C, returns the base dir /d/e
0982: *
0983: * @param weavedClassFileFullPath
0984: * @param weavedClassName
0985: * @return
0986: */
0987: private static String getBaseDir(String weavedClassFileFullPath,
0988: String weavedClassName) {
0989: String baseDirAbsolutePath = weavedClassFileFullPath;
0990: int parentEndIndex = baseDirAbsolutePath
0991: .lastIndexOf(File.separatorChar);
0992: for (int j = weavedClassName.toCharArray().length - 1; j >= 0; j--) {
0993: char c = weavedClassName.toCharArray()[j];
0994: if (c == '.') {
0995: if (parentEndIndex > 0) {
0996: baseDirAbsolutePath = baseDirAbsolutePath
0997: .substring(0, parentEndIndex);
0998: parentEndIndex = baseDirAbsolutePath
0999: .lastIndexOf(File.separatorChar);
1000: }
1001: }
1002: }
1003: if (parentEndIndex > 0) {
1004: baseDirAbsolutePath = baseDirAbsolutePath.substring(0,
1005: parentEndIndex);
1006: }
1007: return baseDirAbsolutePath;
1008: }
1009: }
|