/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.compiler;
import org.codehaus.aspectwerkz.definition.DefinitionLoader;
import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
import org.codehaus.aspectwerkz.joinpoint.management.AdviceInfoContainer;
import org.codehaus.aspectwerkz.util.ContextClassLoader;
import org.codehaus.aspectwerkz.aspect.AdviceInfo;
import org.codehaus.aspectwerkz.cflow.CflowBinding;
import org.codehaus.aspectwerkz.cflow.CflowCompiler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* AspectWerkzC allow for precompilation of class / jar / zip given a class preprocessor. <p/>
* <h2>Usage</h2>
* <p/>
* <pre>
* java [-Daspectwerkz.classloader.preprocessor={ClassPreProcessorImpl}] -cp [...]
* org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] [-keepjp] [-details] [-cp {additional cp i}]* {target
* 1} .. {target n}
* {ClassPreProcessorImpl} : full qualified name of the ClassPreProcessor implementation (must be in classpath)
* defaults to org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor
* {additional cp i} : additionnal classpath needed at compile time (eg: myaspect.jar)
* use as many -cp options as needed
* supports java classpath syntax for classpath separator: ; on windows, : on others
* {target i} : exploded dir, jar, zip files to compile
* Ant 1.5 must be in the classpath
* </pre>
* <p/>
* <p>
* <h2>Classpath note</h2>
* At the beginning of the compilation, all {target i} are added to the classpath automatically. <br/>This is required
* to support caller side advices. <p/>
* <h2>Error handling</h2>
* For each target i, a backup copy is written in ./_aspectwerkzc/i/target <br/>Transformation occurs on original target
* class/dir/jar/zip file <br/>On failure, target backup is restored and stacktrace is given <br/><br/>If
* <i>-haltOnError </i> was set, compilations ends and a <b>complete </b> rollback occurs on all targets, else a status
* report is printed at the end of the compilation, indicating SUCCESS or ERROR for each given target. <br/>If
* <i>-verify </i> was set, all compiled class are verified during the compilation and an error is generated if the
* compiled class bytecode is corrupted. The error is then handled according to the <i>-haltOnError </i> option. <br/>
* <p/>
* <h2>Manifest.mf update</h2>
* The Manifest.mf if present is updated wit the following:
* <ul>
* <li>AspectWerkzC-created: date of the compilation</li>
* <li>AspectWerkzC-preprocessor: full qualified classname of the preprocessor used</li>
* <li>AspectWerkzC-comment: comments</li>
* </ul>
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
*/
public class AspectWerkzC {
// COMMAND LINE OPTIONS
private static final String COMMAND_LINE_OPTION_DASH = "-";
private static final String COMMAND_LINE_OPTION_VERBOSE = "-verbose";
private static final String COMMAND_LINE_OPTION_DETAILS = "-details";
private static final String COMMAND_LINE_OPTION_KEEPJP = "-keepjp";
private static final String COMMAND_LINE_OPTION_HALT = "-haltOnError";
private static final String COMMAND_LINE_OPTION_VERIFY = "-verify";
private static final String COMMAND_LINE_OPTION_CLASSPATH = "-cp";
private static final String COMMAND_LINE_OPTION_TARGETS = "compile.targets";
/**
* option used to defined the class preprocessor
*/
private static final String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
/**
* default class preprocessor
*/
private static final String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
private final static String MF_CUSTOM_DATE = "X-AspectWerkzC-created";
private final static String MF_CUSTOM_PP = "X-AspectWerkzC-preprocessor";
private final static String MF_CUSTOM_COMMENT = "X-AspectWerkzC-comment";
private final static String MF_CUSTOM_COMMENT_VALUE = "AspectWerkzC - AspectWerkz compiler, aspectwerkz.codehaus.org";
private final static SimpleDateFormat DF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final static String BACKUP_DIR = "_aspectwerkzc";
private boolean verify = false;
private boolean keepJp = false;
private boolean haltOnError = false;
private String backupDir = BACKUP_DIR;
/**
* class loader in which the effective compilation occurs, child of system classloader
*/
private URLClassLoader compilationLoader = null;
/**
* class preprocessor instance used to compile targets
*/
private ClassPreProcessor preprocessor = null;
private boolean isAspectWerkzPreProcessor = false;
/**
* index to keep track of {target i} backups
*/
private int sourceIndex;
/**
* Maps the target file to the target backup file
*/
private Map backupMap = new HashMap();
/**
* Maps the target file to a status indicating compilation was successfull
*/
private Map successMap = new HashMap();
private long timer;
/**
* Utility for file manipulation
*/
private Utility utility;
/**
* Construct a new Utility, restore the index for backup
*/
public AspectWerkzC() {
//@todo check for multiple transformation in compiler or in preprocessor ?
sourceIndex = 0;
utility = new Utility();
timer = System.currentTimeMillis();
}
/*
* public void log(String msg) { utility.log(msg); } public void log(String msg, Throwable t) { utility.log(msg);
* t.printStackTrace(); }
*/
public void setVerbose(boolean verbose) {
utility.setVerbose(verbose);
}
public void setKeepJp(boolean keepJp) {
this.keepJp = keepJp;
}
public void setHaltOnError(boolean haltOnError) {
this.haltOnError = haltOnError;
}
public void setVerify(boolean verify) {
this.verify = verify;
}
public void setDetails(boolean details) {
if (details) {
System.setProperty(AW_TRANSFORM_DETAILS, "true");
}
}
public void setBackupDir(String backup) {
this.backupDir = backup;
}
public Utility getUtility() {
return utility;
}
/**
* Sets the ClassPreProcessor implementation to use. <p/>The ClassLoader will be set to System ClassLoader when
* transform(className, byteCode, callerClassLoader) will be called to compile a class.
*/
public void setPreprocessor(String preprocessor) throws CompileException {
try {
Class pp = Class.forName(preprocessor);
this.preprocessor = (ClassPreProcessor) pp.newInstance();
this.preprocessor.initialize();
if (this.preprocessor instanceof AspectWerkzPreProcessor) {
isAspectWerkzPreProcessor = true;
}
} catch (Exception e) {
throw new CompileException("failed to instantiate preprocessor " + preprocessor, e);
}
}
/**
* Backup source file in backup_dir/index/file. The backupMap is updated for further rollback
*/
public void backup(File source, int index) {
// backup source in BACKUP/index dir
File dest = new File(this.backupDir + File.separator + index + File.separator + source.getName());
utility.backupFile(source, dest);
// add to backupMap in case of rollback
backupMap.put(source, dest);
}
/**
* Restore the backup registered
*/
public void restoreBackup() {
for (Iterator i = backupMap.keySet().iterator(); i.hasNext();) {
File source = (File) i.next();
if (!successMap.containsKey(source)) {
File dest = (File) backupMap.get(source);
utility.backupFile(dest, source);
}
}
}
/**
* Delete backup dir at the end of all compilation
*/
public void postCompile(String message) {
restoreBackup();
utility.log(" [backup] removing backup");
utility.deleteDir(new File(this.backupDir));
long ms = Math.max(System.currentTimeMillis() - timer, 1 * 1000);
System.out.println("( " + (int) (ms / 1000) + " s ) " + message);
if (!haltOnError) {
for (Iterator i = backupMap.keySet().iterator(); i.hasNext();) {
File source = (File) i.next();
if (successMap.containsKey(source)) {
System.out.println("SUCCESS: " + source);
} else {
System.out.println("FAILED : " + source);
}
}
}
}
/**
* Compile sourceFile. If prefixPackage is not null, assumes it is the class package information. <p/>Handles :
* <ul>
* <li>directory recursively (exploded jar)</li>
* <li>jar / zip file</li>
* </ul>
*/
public void doCompile(File sourceFile, String prefixPackage) throws CompileException {
if (sourceFile.isDirectory()) {
File[] classes = sourceFile.listFiles();
for (int i = 0; i < classes.length; i++) {
if (classes[i].isDirectory() && !(this.backupDir.equals(classes[i].getName()))) {
String packaging = (prefixPackage != null) ? (prefixPackage + "." + classes[i]
.getName()) : classes[i].getName();
doCompile(classes[i], packaging);
} else if (classes[i].getName().toLowerCase().endsWith(".class")) {
compileClass(classes[i], prefixPackage);
} else if (isJarFile(classes[i])) {
//@todo: jar encountered in a dir - use case ??
compileJar(classes[i]);
}
}
} else if (sourceFile.getName().toLowerCase().endsWith(".class")) {
compileClass(sourceFile, null);
} else if (isJarFile(sourceFile)) {
compileJar(sourceFile);
}
}
/**
* Compiles .class file using fileName as className and given packaging as package name
*/
public void compileClass(File file, String packaging) throws CompileException {
InputStream in = null;
FileOutputStream fos = null;
try {
utility.log(" [compile] " + file.getCanonicalPath());
// dump bytecode in byte[]
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in = new FileInputStream(file);
byte[] buffer = new byte[1024];
while (in.available() > 0) {
int length = in.read(buffer);
if (length == -1) {
break;
}
bos.write(buffer, 0, length);
}
// rebuild className
String className = file.getName().substring(0, file.getName().length() - 6);
if (packaging != null) {
className = packaging + '.' + className;
}
// transform
AspectWerkzPreProcessor.Output out = null;
try {
out = preProcess(preprocessor, className, bos.toByteArray(), compilationLoader);
} catch (Throwable t) {
throw new CompileException("weaver failed for class: " + className, t);
}
// override file
fos = new FileOutputStream(file);
fos.write(out.bytecode);
fos.close();
// if AW and keepjp
if (out.emittedJoinPoints != null && keepJp) {
for (int i = 0; i < out.emittedJoinPoints.length; i++) {
EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
//TODO we assume same package here.. make more generic
String jpClassNoPackage = emittedJoinPoint.getJoinPointClassName();
if (jpClassNoPackage.indexOf('/')>0) {
jpClassNoPackage = jpClassNoPackage.substring(jpClassNoPackage.lastIndexOf('/'));
}
File jpFile = new File(file.getParent(), jpClassNoPackage+".class");
utility.log(" [keepjp] " + jpFile.getCanonicalPath());
FileOutputStream jpFos = new FileOutputStream(jpFile);
JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(emittedJoinPoint, compilationLoader);
jpFos.write(compiledJp.bytecode);
jpFos.close();
// handle cflow if any
CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
if (compiledCflowAspects.length > 0) {
String baseDirAbsolutePath = getBaseDir(file.getCanonicalPath(), className);
for (int j = 0; j < compiledCflowAspects.length; j++) {
CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
File cflowFile = new File(baseDirAbsolutePath + File.separatorChar + compiledCflowAspect.className.replace('/', File.separatorChar) + ".class");
(new File(cflowFile.getParent())).mkdirs();
utility.log(" [keepjp] (cflow) " + cflowFile.getCanonicalPath());
FileOutputStream cflowFos = new FileOutputStream(cflowFile);
cflowFos.write(compiledCflowAspect.bytecode);
cflowFos.close();
}
}
}
}
// verify modified class
if (verify) {
URLClassLoader verifier = new VerifierClassLoader(
compilationLoader.getURLs(),
ClassLoader.getSystemClassLoader()
);
try {
utility.log(" [verify] " + className);
Class.forName(className, false, verifier);
} catch (Throwable t) {
utility.log(" [verify] corrupted class: " + className);
throw new CompileException("corrupted class: " + className, t);
}
}
} catch (IOException e) {
throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
} finally {
try {
in.close();
} catch (Throwable e) {
;
}
try {
fos.close();
} catch (Throwable e) {
;
}
}
}
/**
* Compile all .class encountered in the .jar/.zip file. <p/>The target.jar is compiled in the
* target.jar.aspectwerkzc and the target.jar.aspectwerkzc then overrides target.jar on success.
*/
public void compileJar(File file) throws CompileException {
utility.log(" [compilejar] " + file.getAbsolutePath());
// create an empty jar target.jar.aspectwerkzc
File workingFile = new File(file.getAbsolutePath() + ".aspectwerkzc");
if (workingFile.exists()) {
workingFile.delete();
}
ZipFile zip = null;
ZipOutputStream zos = null;
try {
zip = new ZipFile(file);
zos = new ZipOutputStream(new FileOutputStream(workingFile));
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry ze = (ZipEntry) e.nextElement();
// dump bytes read in byte[]
InputStream in = zip.getInputStream(ze);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (in.available() > 0) {
int length = in.read(buffer);
if (length == -1) {
break;
}
bos.write(buffer, 0, length);
}
in.close();
// transform only .class file
AspectWerkzPreProcessor.Output out = null;
byte[] transformed = null;
if (ze.getName().toLowerCase().endsWith(".class")) {
utility.log(" [compilejar] compile " + file.getName() + ":" + ze.getName());
String className = ze.getName().substring(0, ze.getName().length() - 6);
try {
out = preProcess(preprocessor, className, bos.toByteArray(), compilationLoader);
transformed = out.bytecode;
} catch (Throwable t) {
throw new CompileException("weaver failed for class: " + className, t);
}
} else {
out = null;
transformed = bos.toByteArray();
}
// customize Manifest.mf
if (ze.getName().toLowerCase().equals("meta-inf/manifest.mf")) {
try {
Manifest mf = new Manifest(new ByteArrayInputStream(transformed));
Attributes at = mf.getMainAttributes();
at.putValue(MF_CUSTOM_DATE, DF.format(new Date()));
at.putValue(MF_CUSTOM_PP, preprocessor.getClass().getName());
at.putValue(MF_CUSTOM_COMMENT, MF_CUSTOM_COMMENT_VALUE);
// re read the updated manifest
bos.reset();
mf.write(bos);
transformed = bos.toByteArray();
} catch (Exception emf) {
emf.printStackTrace();
}
}
// update target.jar.aspectwerkzc working file
ZipEntry transformedZe = new ZipEntry(ze.getName());
transformedZe.setSize(transformed.length);
CRC32 crc = new CRC32();
crc.update(transformed);
transformedZe.setCrc(crc.getValue());
transformedZe.setMethod(ze.getMethod());
zos.putNextEntry(transformedZe);
zos.write(transformed, 0, transformed.length);
// if AW and keepjp
if (keepJp && out != null && out.emittedJoinPoints!=null) {
for (int i = 0; i < out.emittedJoinPoints.length; i++) {
EmittedJoinPoint emittedJoinPoint = out.emittedJoinPoints[i];
JoinPointManager.CompiledJoinPoint compiledJp = compileJoinPoint(emittedJoinPoint, compilationLoader);
utility.log(" [compilejar] (keepjp) " + file.getName() + ":" + emittedJoinPoint.getJoinPointClassName());
ZipEntry jpZe = new ZipEntry(emittedJoinPoint.getJoinPointClassName()+".class");
jpZe.setSize(compiledJp.bytecode.length);
CRC32 jpCrc = new CRC32();
jpCrc.update(compiledJp.bytecode);
jpZe.setCrc(jpCrc.getValue());
jpZe.setMethod(ze.getMethod());
zos.putNextEntry(jpZe);
zos.write(compiledJp.bytecode, 0, compiledJp.bytecode.length);
CflowCompiler.CompiledCflowAspect[] compiledCflowAspects = compileCflows(compiledJp);
if (compiledCflowAspects.length > 0) {
for (int j = 0; j < compiledCflowAspects.length; j++) {
CflowCompiler.CompiledCflowAspect compiledCflowAspect = compiledCflowAspects[j];
utility.log(" [compilejar] (keepjp) (cflow) " + file.getName() + ":" + compiledCflowAspect.className);
ZipEntry cflowZe = new ZipEntry(compiledCflowAspect.className+".class");
cflowZe.setSize(compiledCflowAspect.bytecode.length);
CRC32 cflowCrc = new CRC32();
cflowCrc.update(compiledCflowAspect.bytecode);
cflowZe.setCrc(cflowCrc.getValue());
cflowZe.setMethod(ze.getMethod());
zos.putNextEntry(cflowZe);
zos.write(compiledCflowAspect.bytecode, 0, compiledCflowAspect.bytecode.length);
}
}
}
}
}
zip.close();
zos.close();
// replace file by workingFile
File swap = new File(file.getAbsolutePath() + ".swap.aspectwerkzc");
utility.backupFile(file, swap);
try {
utility.backupFile(workingFile, new File(file.getAbsolutePath()));
workingFile.delete();
swap.delete();
} catch (Exception e) {
// restore swapFile
utility.backupFile(swap, new File(file.getAbsolutePath()));
workingFile.delete();
throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
}
} catch (IOException e) {
throw new CompileException("compile " + file.getAbsolutePath() + " failed", e);
} finally {
try {
zos.close();
} catch (Throwable e) {
;
}
try {
zip.close();
} catch (Throwable e) {
;
}
}
}
/**
* Compile given target.
*
* @return false if process should stop
*/
public boolean compile(File source) {
sourceIndex++;
backup(source, sourceIndex);
try {
doCompile(source, null);
} catch (CompileException e) {
utility.log(" [aspectwerkzc] compilation encountered an error");
e.printStackTrace();
return (!haltOnError);
}
// compile sucessfull
successMap.put(source, Boolean.TRUE);
return true;
}
/**
* Set up the compilation path by building a URLClassLoader with all targets in
*
* @param targets to add to compilationLoader classpath
* @param parentLoader the parent ClassLoader used by the new one
*/
public void setCompilationPath(File[] targets, ClassLoader parentLoader) {
URL[] urls = new URL[targets.length];
int j = 0;
for (int i = 0; i < targets.length; i++) {
try {
urls[j] = targets[i].getCanonicalFile().toURL();
j++;
} catch (IOException e) {
System.err.println("bad target " + targets[i]);
}
}
compilationLoader = new URLClassLoader(urls, parentLoader);
}
/**
* Test if file is a zip/jar file
*/
public static boolean isJarFile(File source) {
return (source.isFile() && (source.getName().toLowerCase().endsWith(".jar") || source
.getName().toLowerCase().endsWith(".zip")));
}
/**
* Usage message
*/
public static void doHelp() {
System.out.println("--- AspectWerkzC compiler ---");
System.out.println("Usage:");
System.out
.println(
"java -cp ... org.codehaus.aspectwerkz.compiler.AspectWerkzC [-verbose] [-haltOnError] [-verify] <target 1> .. <target n>"
);
System.out.println(" <target i> : exploded dir, jar, zip files to compile");
}
/**
* Creates and configures an AspectWerkzC compiler.
*
* @param params a map containing the compiler parameters
* @return a new and configured <CODE>AspectWerkzC</CODE>
*/
private static AspectWerkzC createCompiler(Map params) {
AspectWerkzC compiler = new AspectWerkzC();
for (Iterator it = params.entrySet().iterator(); it.hasNext();) {
Map.Entry param = (Map.Entry) it.next();
if (COMMAND_LINE_OPTION_VERBOSE.equals(param.getKey())) {
compiler.setVerbose(Boolean.TRUE.equals(param.getValue()));
} else if (COMMAND_LINE_OPTION_HALT.equals(param.getKey())) {
compiler.setHaltOnError(Boolean.TRUE.equals(param.getValue()));
} else if (COMMAND_LINE_OPTION_VERIFY.equals(param.getKey())) {
compiler.setVerify(Boolean.TRUE.equals(param.getValue()));
} else if (COMMAND_LINE_OPTION_KEEPJP.equals(param.getKey())) {
compiler.setKeepJp(Boolean.TRUE.equals(param.getValue()));
} else if (COMMAND_LINE_OPTION_DETAILS.equals(param.getKey())) {
compiler.setDetails(Boolean.TRUE.equals(param.getValue()));
}
}
return compiler;
}
/**
* Runs the AspectWerkzC compiler for the <tt>targets</tt> files.
*
* @param compiler a configured <CODE>AspectWerkzC</CODE>
* @param classLoader the class loader to be used
* @param preProcessor fully qualified name of the preprocessor class.
* If <tt>null</tt> than the default is used
* (<CODE>org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor</CODE>)
* @param classpath list of Files representing the classpath (List<File>)
* @param targets the list of target files (List<File>)
*/
public static void compile(AspectWerkzC compiler,
ClassLoader classLoader,
String preProcessor,
List classpath,
List targets) {
List fullPath = new ArrayList();
if (classpath != null) {
fullPath.addAll(classpath);
}
fullPath.addAll(targets);
compiler.setCompilationPath((File[]) fullPath.toArray(new File[fullPath.size()]), classLoader);
Thread.currentThread().setContextClassLoader(compiler.compilationLoader);
// AOPC special fix
// turn off -Daspectwerkz.definition.file registration and register it at the
// compilationLoader level instead
SystemDefinitionContainer.disableSystemWideDefinition();
SystemDefinitionContainer.deployDefinitions(
compiler.compilationLoader,
DefinitionLoader.getDefaultDefinition(compiler.compilationLoader)
);
String preprocessorFqn = preProcessor == null ? PRE_PROCESSOR_CLASSNAME_DEFAULT
: preProcessor;
try {
compiler.setPreprocessor(preprocessorFqn);
} catch (CompileException e) {
System.err.println("Cannot instantiate ClassPreProcessor: " + preprocessorFqn);
e.printStackTrace();
System.exit(-1);
}
cleanBackupDir(compiler);
for (Iterator i = targets.iterator(); i.hasNext();) {
if (!compiler.compile((File) i.next())) {
compiler.postCompile("*** An error occured ***");
System.exit(-1);
}
}
compiler.postCompile("");
}
private static void cleanBackupDir(AspectWerkzC compiler) {
// prepare backup directory
try {
File temp = new File(compiler.backupDir);
if (temp.exists()) {
compiler.getUtility().deleteDir(temp);
}
temp.mkdir();
(new File(temp, "" + System.currentTimeMillis() + ".timestamp")).createNewFile();
} catch (Exception e) {
System.err.println("failed to prepare backup dir: " + compiler.backupDir);
e.printStackTrace();
System.exit(-1);
}
}
public static void main(String[] args) {
if (args.length <= 0) {
doHelp();
return; //stop here
}
Map options = parseOptions(args);
AspectWerkzC compiler = createCompiler(options);
compiler.setBackupDir(BACKUP_DIR);
compile(
compiler,
ClassLoader.getSystemClassLoader(),
System.getProperty(
PRE_PROCESSOR_CLASSNAME_PROPERTY,
PRE_PROCESSOR_CLASSNAME_DEFAULT
),
(List) options.get(COMMAND_LINE_OPTION_CLASSPATH),
(List) options.get(COMMAND_LINE_OPTION_TARGETS)
);
}
private static Map parseOptions(String[] args) {
Map options = new HashMap();
List targets = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (COMMAND_LINE_OPTION_VERBOSE.equals(args[i])) {
options.put(COMMAND_LINE_OPTION_VERBOSE, Boolean.TRUE);
} else if (COMMAND_LINE_OPTION_KEEPJP.equals(args[i])) {
options.put(COMMAND_LINE_OPTION_KEEPJP, Boolean.TRUE);
} else if (COMMAND_LINE_OPTION_DETAILS.equals(args[i])) {
options.put(COMMAND_LINE_OPTION_DETAILS, Boolean.TRUE);
} else if (COMMAND_LINE_OPTION_HALT.equals(args[i])) {
options.put(COMMAND_LINE_OPTION_HALT, Boolean.TRUE);
} else if (COMMAND_LINE_OPTION_VERIFY.equals(args[i])) {
options.put(COMMAND_LINE_OPTION_VERIFY, Boolean.TRUE);
} else if (COMMAND_LINE_OPTION_CLASSPATH.equals(args[i])) {
if (i == (args.length - 1)) {
continue; //FIXME: this is an error
} else {
options.put(
COMMAND_LINE_OPTION_CLASSPATH,
toFileArray(args[++i], File.pathSeparator)
);
}
} else if (args[i].startsWith(COMMAND_LINE_OPTION_DASH)) {
; // nothing to be done about it
} else {
File file = toFile(args[i]);
if (file == null) {
System.err.println("Ignoring inexistant target: " + args[i]);
} else {
targets.add(file);
}
}
}
options.put(COMMAND_LINE_OPTION_TARGETS, targets);
return options;
}
private static List toFileArray(String str, String sep) {
if (str == null || str.length() == 0) {
return new ArrayList();
}
List files = new ArrayList();
int start = 0;
int idx = str.indexOf(sep, start);
int len = sep.length();
while (idx != -1) {
files.add(new File(str.substring(start, idx)));
start = idx + len;
idx = str.indexOf(sep, start);
}
files.add(new File(str.substring(start)));
return files;
}
private static File toFile(String path) {
File file = new File(path);
return file.exists() ? file : null;
}
/**
* Helper method to have the emitted joinpoint back when dealing with AspectWerkz pp
* @param preProcessor
* @param className
* @param bytecode
* @param compilationLoader
* @return
*/
private AspectWerkzPreProcessor.Output preProcess(ClassPreProcessor preProcessor, String className, byte[] bytecode, ClassLoader compilationLoader) {
if (isAspectWerkzPreProcessor) {
return ((AspectWerkzPreProcessor)preProcessor).preProcessWithOutput(className, bytecode, compilationLoader);
} else {
byte[] newBytes = preProcessor.preProcess(className, bytecode, compilationLoader);
AspectWerkzPreProcessor.Output out = new AspectWerkzPreProcessor.Output();
out.bytecode = newBytes;
return out;
}
}
/**
* Handles the compilation of the given emitted joinpoint
*
* @param emittedJoinPoint
* @param loader
* @return
* @throws IOException
*/
private JoinPointManager.CompiledJoinPoint compileJoinPoint(EmittedJoinPoint emittedJoinPoint, ClassLoader loader) throws IOException {
try {
Class callerClass = ContextClassLoader.forName(emittedJoinPoint.getCallerClassName().replace('/', '.'));
Class calleeClass = ContextClassLoader.forName(emittedJoinPoint.getCalleeClassName().replace('/', '.'));
JoinPointManager.CompiledJoinPoint jp = JoinPointManager.compileJoinPoint(
emittedJoinPoint.getJoinPointType(),
callerClass,
emittedJoinPoint.getCallerMethodName(),
emittedJoinPoint.getCallerMethodDesc(),
emittedJoinPoint.getCallerMethodModifiers(),
emittedJoinPoint.getCalleeClassName(),
emittedJoinPoint.getCalleeMemberName(),
emittedJoinPoint.getCalleeMemberDesc(),
emittedJoinPoint.getCalleeMemberModifiers(),
emittedJoinPoint.getJoinPointHash(),
emittedJoinPoint.getJoinPointClassName(),
calleeClass,
loader
);
return jp;
} catch (ClassNotFoundException e) {
throw new IOException("Could not compile joinpoint : " + e.toString());
}
}
/**
* Handles the compilation of the possible cflowAspect associated to the advices that affects the given
* joinpoint
*
* @param jp
* @return
*/
private CflowCompiler.CompiledCflowAspect[] compileCflows(JoinPointManager.CompiledJoinPoint jp) {
List allCflowBindings = new ArrayList();
AdviceInfoContainer adviceInfoContainer = jp.compilationInfo.getInitialModel().getAdviceInfoContainer();
AdviceInfo[] advices = adviceInfoContainer.getAllAdviceInfos();
for (int i = 0; i < advices.length; i++) {
AdviceInfo adviceInfo = advices[i];
List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceInfo.getExpressionInfo());
allCflowBindings.addAll(cflowBindings);
}
List compiledCflows = new ArrayList();
for (Iterator iterator = allCflowBindings.iterator(); iterator.hasNext();) {
CflowBinding cflowBinding = (CflowBinding) iterator.next();
compiledCflows.add(CflowCompiler.compileCflowAspect(cflowBinding.getCflowID()));
}
return (CflowCompiler.CompiledCflowAspect[])compiledCflows.toArray(new CflowCompiler.CompiledCflowAspect[0]);
}
/**
* Given a path d/e/a/b/C.class and a class a.b.C, returns the base dir /d/e
*
* @param weavedClassFileFullPath
* @param weavedClassName
* @return
*/
private static String getBaseDir(String weavedClassFileFullPath, String weavedClassName) {
String baseDirAbsolutePath = weavedClassFileFullPath;
int parentEndIndex = baseDirAbsolutePath.lastIndexOf(File.separatorChar);
for (int j = weavedClassName.toCharArray().length-1; j >= 0; j--) {
char c = weavedClassName.toCharArray()[j];
if (c == '.') {
if (parentEndIndex > 0) {
baseDirAbsolutePath = baseDirAbsolutePath.substring(0, parentEndIndex);
parentEndIndex = baseDirAbsolutePath.lastIndexOf(File.separatorChar);
}
}
}
if (parentEndIndex > 0) {
baseDirAbsolutePath = baseDirAbsolutePath.substring(0, parentEndIndex);
}
return baseDirAbsolutePath;
}
}