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