Generate bind and unbind methods for references from fields using asm byte code manipulation.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@572281 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scrplugin/pom.xml b/scrplugin/pom.xml
index dfac7ef..ad773da 100644
--- a/scrplugin/pom.xml
+++ b/scrplugin/pom.xml
@@ -55,6 +55,13 @@
<version>1.0</version>
<scope>compile</scope>
</dependency>
+
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>3.0</version>
+ </dependency>
+
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
diff --git a/scrplugin/src/main/java/org/apache/felix/scrplugin/SCRDescriptorMojo.java b/scrplugin/src/main/java/org/apache/felix/scrplugin/SCRDescriptorMojo.java
index ac7625d..c351e4f 100644
--- a/scrplugin/src/main/java/org/apache/felix/scrplugin/SCRDescriptorMojo.java
+++ b/scrplugin/src/main/java/org/apache/felix/scrplugin/SCRDescriptorMojo.java
@@ -42,7 +42,6 @@
import org.apache.felix.scrplugin.tags.JavaField;
import org.apache.felix.scrplugin.tags.JavaTag;
import org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription;
-import org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription.Modification;
import org.apache.felix.scrplugin.xml.ComponentDescriptorIO;
import org.apache.felix.scrplugin.xml.MetaTypeIO;
import org.apache.maven.model.Resource;
@@ -58,7 +57,7 @@
* generates a service descriptor file based on annotations found in the sources.
*
* @goal scr
- * @phase generate-resources
+ * @phase process-classes
* @description Build Service Descriptors from Java Source
* @requiresDependencyResolution compile
*/
@@ -94,8 +93,18 @@
*/
private String metaTypeName;
+ /**
+ * This flag controls the generation of the bind/unbind methods.
+ * @parameter default-value="true"
+ */
+ private boolean generateAccessors;
+
+ /**
+ * @see org.apache.maven.plugin.AbstractMojo#execute()
+ */
public void execute() throws MojoExecutionException, MojoFailureException {
this.getLog().debug("Starting SCRDescriptorMojo....");
+ this.getLog().debug("..generating accessors: " + this.generateAccessors);
boolean hasFailures = false;
@@ -517,38 +526,21 @@
}
// if this is a field we look for the bind/unbind methods
// and create them if they are not availabe
- if ( false ) {
+ if ( this.generateAccessors ) {
if ( reference.getField() != null && component.getJavaClassDescription() instanceof ModifiableJavaClassDescription ) {
- String changed = null;
+ boolean createBind = false;
+ boolean createUnbind = false;
// Only create method if no bind name has been specified
if ( bindValue == null && ref.findMethod(ref.getBind()) == null ) {
// create bind method
- final String realMethodName = "bind" + Character.toUpperCase(name.charAt(0))
- + name.substring(1);
- changed = ((ModifiableJavaClassDescription)component.getJavaClassDescription()).addProtectedMethod(
- realMethodName,
- type,
- "{this." + reference.getField().getName() + "=param;}");
+ createBind = true;
}
if ( unbindValue == null && ref.findMethod(ref.getUnbind()) == null ) {
// create unbind method
- final String realMethodName = "unbind" + Character.toUpperCase(name.charAt(0))
- + name.substring(1);
- final String c = ((ModifiableJavaClassDescription)component.getJavaClassDescription()).addProtectedMethod(
- realMethodName,
- type,
- "{this." + reference.getField().getName() + "=null;}");
- if ( changed == null ) {
- changed = c;
- } else {
- changed = changed + c;
- }
+ createUnbind = true;
}
- if ( changed != null ) {
- Modification mod = new Modification();
- mod.lineNumber = 10;
- mod.content = changed;
- ((ModifiableJavaClassDescription)component.getJavaClassDescription()).writeClassFile(new Modification[] {mod});
+ if ( createBind || createUnbind ) {
+ ((ModifiableJavaClassDescription)component.getJavaClassDescription()).addMethods(name, type, createBind, createUnbind);
}
}
}
diff --git a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/JavaClassDescriptorManager.java b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/JavaClassDescriptorManager.java
index 2d8a748..4be1e6d 100644
--- a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/JavaClassDescriptorManager.java
+++ b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/JavaClassDescriptorManager.java
@@ -74,6 +74,9 @@
/** The component definitions from other bundles hashed by classname. */
protected final Map componentDescriptions = new HashMap();
+ /** The maven project. */
+ protected final MavenProject project;
+
/**
* Construct a new manager.
* @param log
@@ -85,6 +88,7 @@
final MavenProject project)
throws MojoFailureException, MojoExecutionException {
this.log = log;
+ this.project = project;
this.classloader = this.getCompileClassLoader(project);
// get all the class sources through qdox
@@ -177,6 +181,13 @@
}
/**
+ * Return the project.
+ */
+ public MavenProject getProject() {
+ return this.project;
+ }
+
+ /**
* Read the service component description.
* @param artifact
* @param entry
diff --git a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/ModifiableJavaClassDescription.java b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/ModifiableJavaClassDescription.java
index 28986f8..91a39fa 100644
--- a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/ModifiableJavaClassDescription.java
+++ b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/ModifiableJavaClassDescription.java
@@ -18,15 +18,11 @@
*/
package org.apache.felix.scrplugin.tags;
+import org.apache.maven.plugin.MojoExecutionException;
+
public interface ModifiableJavaClassDescription {
- String addProtectedMethod(String name, String paramType, String contents);
-
- void writeClassFile(Modification[] mods);
-
- public static final class Modification {
- public int lineNumber;
- public String content;
- }
+ void addMethods(String propertyName, String className, boolean createBind, boolean createUnbind)
+ throws MojoExecutionException;
}
diff --git a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/qdox/QDoxJavaClassDescription.java b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/qdox/QDoxJavaClassDescription.java
index bccf47d..33719bc 100644
--- a/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/qdox/QDoxJavaClassDescription.java
+++ b/scrplugin/src/main/java/org/apache/felix/scrplugin/tags/qdox/QDoxJavaClassDescription.java
@@ -18,9 +18,9 @@
*/
package org.apache.felix.scrplugin.tags.qdox;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.LineNumberReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -32,6 +32,11 @@
import org.apache.felix.scrplugin.tags.JavaTag;
import org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription;
import org.apache.maven.plugin.MojoExecutionException;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
@@ -227,47 +232,70 @@
}
/**
- * @see org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription#addProtectedMethod(java.lang.String, java.lang.String, java.lang.String)
+ * @see org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription#addMethods(java.lang.String, java.lang.String, boolean, boolean)
*/
- public String addProtectedMethod(String name, String paramType, String contents) {
- final JavaParameter param = new JavaParameter(new Type(paramType), "param");
+ public void addMethods(String propertyName,
+ String className,
+ boolean createBind,
+ boolean createUnbind)
+ throws MojoExecutionException {
+ // now do byte code manipulation
+ final String targetDirectory = this.manager.getProject().getBuild().getOutputDirectory();
+ final String fileName = targetDirectory + File.separatorChar + this.getName().replace('.', File.separatorChar) + ".class";
+ final ClassNode cn = new ClassNode();
+ try {
+ final ClassReader reader = new ClassReader(new FileInputStream(fileName));
+ reader.accept(cn, 0);
+
+ final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cn.accept(writer);
+ this.createBind(writer, propertyName, className);
+ this.createUnbind(writer, propertyName, className);
+
+ final FileOutputStream fos = new FileOutputStream(fileName);
+ fos.write(writer.toByteArray());
+ fos.close();
+ } catch (Exception e) {
+ throw new MojoExecutionException("Unable to add methods to " + this.getName(), e);
+ }
+ }
+
+ protected void createBind(ClassWriter cw, String propertyName, String typeName) {
+ final org.objectweb.asm.Type type = org.objectweb.asm.Type.getType("L" + typeName + ";");
+ final String methodName = "bind" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, methodName, "(" + type.toString() + ")V", null, null);
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), 1);
+ mv.visitFieldInsn(Opcodes.PUTFIELD, this.getName(), propertyName, type.toString());
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ // add to qdox
+ final JavaParameter param = new JavaParameter(new Type(typeName), "param");
final JavaParameter[] params = new JavaParameter[] {param};
final com.thoughtworks.qdox.model.JavaMethod meth = new com.thoughtworks.qdox.model.JavaMethod();
- meth.setName(name);
- meth.setSourceCode(contents);
+ meth.setName(methodName);
meth.setParameters(params);
meth.setModifiers(new String[] {"protected"});
this.javaClass.addMethod(meth);
- return "protected void " + name + "(" + paramType + " param)" + contents + " ";
- }
+ }
- /**
- * @see org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription#writeClassFile(org.apache.felix.scrplugin.tags.ModifiableJavaClassDescription.Modification[])
- */
- public void writeClassFile(Modification[] mods) {
- if ( mods != null && mods.length > 0 ) {
- try {
- final LineNumberReader reader = new LineNumberReader(new FileReader(this.source.getFile()));
- for(int i=0; i<mods.length; i++) {
- int lineNumber = mods[i].lineNumber;
- while ( reader.getLineNumber() < lineNumber ) {
- final String line = reader.readLine();
- System.out.println(line);
- }
- final String line = reader.readLine();
- System.out.print(line);
- System.out.println(mods[i].content);
- }
- String line;
- while ( (line = reader.readLine()) != null ) {
- System.out.println(line);
- }
- reader.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
+ protected void createUnbind(ClassWriter cw, String propertyName, String typeName) {
+ final org.objectweb.asm.Type type = org.objectweb.asm.Type.getType("L" + typeName + ";");
+ final String methodName = "unbind" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, methodName, "(" + type.toString() + ")V", null, null);
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ mv.visitFieldInsn(Opcodes.PUTFIELD, this.getName(), propertyName, type.toString());
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ // add to qdox
+ final JavaParameter param = new JavaParameter(new Type(typeName), "param");
+ final JavaParameter[] params = new JavaParameter[] {param};
+ final com.thoughtworks.qdox.model.JavaMethod meth = new com.thoughtworks.qdox.model.JavaMethod();
+ meth.setName(methodName);
+ meth.setParameters(params);
+ meth.setModifiers(new String[] {"protected"});
+ this.javaClass.addMethod(meth);
+ }
}