package pt.ist.fenixframework.pstm;
import java.io.File;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import jvstm.ProcessAtomicAnnotations;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class PostProcessDomainClasses extends AbstractDomainPostProcessor {
private static final String OID_INNER_CLASS_INTERNAL_NAME = Type.getInternalName(DomainObjectAllocator.OID.class);
private static final String CONSTRUCTOR_DESC = Type.getMethodDescriptor(Type.VOID_TYPE,
new Type[] { Type.getType(DomainObjectAllocator.OID.class) });
private PostProcessDomainClasses() {
super();
}
public PostProcessDomainClasses(List<URL> dmlFiles, String classFullName, String domainModelClassName) {
this(dmlFiles, classFullName, domainModelClassName, Thread.currentThread().getContextClassLoader());
}
public PostProcessDomainClasses(List<URL> dmlFiles, String classFullName, String domainModelClassName,
ClassLoader parentClassLoader) {
super(parentClassLoader);
this.dmlFiles.addAll(dmlFiles);
this.classFullName = classFullName;
this.domainModelClassName = domainModelClassName;
}
public static void main(final String args[]) throws MalformedURLException {
PostProcessDomainClasses loader = new PostProcessDomainClasses();
loader.processArgs(args);
loader.start();
// process, also, the @Atomic annotations
ProcessAtomicAnnotations processor = new ProcessAtomicAnnotations(Transaction.class, new String[] { "." });
processor.start();
}
@Override
protected ClassVisitor makeNewClassVisitor(ClassWriter cw) {
return new AddOJBConstructorClassAdapter(cw);
}
@Override
protected void finishedProcessingClass(URL classURL, byte[] classBytecode) {
super.finishedProcessingClass(classURL, classBytecode);
// No need to post-process classes that are already post-processed
if (classURL.toExternalForm().startsWith("jar:file")) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(new File(classURL.toURI()));
fos.write(classBytecode);
fos.close();
} catch (Exception e) {
throw new Error("Couldn't rewrite class file: " + e);
}
}
class AddOJBConstructorClassAdapter extends ClassAdapter implements Opcodes {
private String classDesc = null;
private String superDesc = null;
private boolean foundConstructor = false;
private boolean foundInnerClass = false;
private boolean warnOnFiels = false;
public AddOJBConstructorClassAdapter(ClassVisitor cv) {
super(cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.classDesc = name;
this.superDesc = superName;
this.warnOnFiels = isDomainNonBaseClass(descToName(classDesc));
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (!foundInnerClass) {
foundInnerClass = OID_INNER_CLASS_INTERNAL_NAME.equals(name);
}
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (warnOnFiels && ((access & ACC_STATIC) == 0)) {
System.err.println(classDesc + ": field not declared on base class -> " + name);
}
return super.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
if (!foundConstructor) {
// force it
visitMethod(ACC_PUBLIC, "<init>", CONSTRUCTOR_DESC, null, null);
}
if (!foundInnerClass) {
// add, also, the InnerClasses attribute for the
// DomainObjectAllocator.OID class that is used in the
// constructor injected
visitInnerClass(OID_INNER_CLASS_INTERNAL_NAME, Type.getInternalName(DomainObjectAllocator.class), "OID",
ACC_PUBLIC + ACC_STATIC);
}
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("<init>".equals(name) && CONSTRUCTOR_DESC.equals(desc)) {
// we process it and remove the original, if any, by returning
// null
mv.visitCode();
// all the domain objects must inherit an allocate-instance only
// constructor
// in the present case, it is declared on the
// AbstractDomainObject class
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, superDesc, "<init>", CONSTRUCTOR_DESC);
if (isDomainBaseClass(descToName(classDesc))) {
// for base classes, we must invoke the initInstance()
// method
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, classDesc, "initInstance", "()V");
}
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
foundConstructor = true;
return null;
} else {
return mv;
}
}
}
}