You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2020/06/11 02:43:57 UTC

[tomee-patch-plugin] 02/02: Initial code forked from https://github.com/tomitribe/jkta

This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-patch-plugin.git

commit 2a06d9f081c1db05164286db9149ba17c30754dc
Author: David Blevins <da...@gmail.com>
AuthorDate: Wed Jun 10 19:33:59 2020 -0700

    Initial code forked from https://github.com/tomitribe/jkta
    
    This is a temporary codebase with the goal of getting a completely
    running TomEE 8 under the jakarta namespace as soon as possible and
    without any overhead of abstractions or dealing with third-party
    libraries or third-party communities.
    
    This is intended as a stop-gap and this code base will not live
    long-term.  Any improvements will be migrated to either the Eclipse
    Transformer or Jkta, hopefully making this codebase not necessary.
---
 pom.xml                                            |  15 +-
 tomee-patch-core/pom.xml                           |  55 ++-
 .../tomee/patch/core/AnnotationTransformer.java    |  37 ++
 .../apache/tomee/patch/core/ClassTransformer.java  |  60 +++
 .../java/org/apache/tomee/patch/core/Copy.java     |  41 ++
 .../apache/tomee/patch/core/FieldTransformer.java  |  38 ++
 .../main/java/org/apache/tomee/patch/core/Is.java  |  39 ++
 .../apache/tomee/patch/core/MethodTransformer.java | 118 ++++++
 .../apache/tomee/patch/core/ModuleTransformer.java |  26 ++
 .../tomee/patch/core/SignatureTransformer.java     |  25 ++
 .../apache/tomee/patch/core/Transformation.java    | 101 +++++
 .../src/test/java/javax/ejb/Process.java           |  27 ++
 .../java/javax/enterprise/context/MockScoped.java  |  41 ++
 .../src/test/java/javax/jms/Consumer.java          |  26 ++
 .../java/javax/jms/EnterpriseBeanConsumer.java     |  28 ++
 .../test/java/javax/persistence/EntityBean.java    |  27 ++
 .../src/test/java/javax/persistence/Persist.java   |  31 ++
 .../patch/core/AnnotationTransformerTest.java      | 140 +++++++
 .../org/apache/tomee/patch/core/ArrayData.java     |  48 +++
 .../java/org/apache/tomee/patch/core/Asmifier.java | 168 ++++++++
 .../java/org/apache/tomee/patch/core/Bytecode.java |  62 +++
 .../tomee/patch/core/ClassTransformerTest.java     | 285 +++++++++++++
 .../java/org/apache/tomee/patch/core/Data.java     |  35 ++
 .../tomee/patch/core/FieldTransformerTest.java     |  95 +++++
 .../tomee/patch/core/MethodTransformerTest.java    | 445 +++++++++++++++++++++
 .../tomee/patch/core/ModuleTransformerTest.java    |  21 +
 .../java/org/apache/tomee/patch/core/Scan.java     |  70 ++++
 .../tomee/patch/core/SignatureTransformerTest.java |  38 ++
 .../org/apache/tomee/patch/core/Transform.java     |  78 ++++
 tomee-patch-plugin/pom.xml                         |  88 ++++
 .../apache/tomee/patch/plugin/TransformMojo.java   |  33 ++
 31 files changed, 2339 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index de443de..d9ceb64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,6 @@
     <relativePath><!--Resolve on repository--></relativePath>
   </parent>
 
-
   <groupId>org.apache.tomee.patch</groupId>
   <artifactId>tomee-patch-parent</artifactId>
   <packaging>pom</packaging>
@@ -84,5 +83,19 @@
     <module>tomee-patch-core</module>
     <module>tomee-patch-plugin</module>
   </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
 </project>
 
diff --git a/tomee-patch-core/pom.xml b/tomee-patch-core/pom.xml
index 93e381c..9ab050c 100644
--- a/tomee-patch-core/pom.xml
+++ b/tomee-patch-core/pom.xml
@@ -29,6 +29,59 @@
   </parent>
 
   <artifactId>tomee-patch-core</artifactId>
-  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>8.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-tree</artifactId>
+      <version>8.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-analysis</artifactId>
+      <version>8.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-commons</artifactId>
+      <version>8.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-util</artifactId>
+      <version>8.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.tomitribe</groupId>
+      <artifactId>tomitribe-crest</artifactId>
+      <version>0.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.tomitribe</groupId>
+      <artifactId>tomitribe-crest-xbean</artifactId>
+      <version>0.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.tomitribe</groupId>
+      <artifactId>tomitribe-util</artifactId>
+      <version>1.3.13</version>
+    </dependency>
+    <dependency>
+      <groupId>org.tomitribe.jkta</groupId>
+      <artifactId>jkta</artifactId>
+      <version>0.10</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
 
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/AnnotationTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/AnnotationTransformer.java
new file mode 100644
index 0000000..5269b93
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/AnnotationTransformer.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.AnnotationVisitor;
+
+public class AnnotationTransformer extends AnnotationVisitor {
+
+    public AnnotationTransformer(final int api, final AnnotationVisitor annotationVisitor) {
+        super(api, annotationVisitor);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
+        return new AnnotationTransformer(this.api, super.visitAnnotation(name, descriptor));
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(final String name) {
+        return new AnnotationTransformer(this.api, super.visitArray(name));
+    }
+
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ClassTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ClassTransformer.java
new file mode 100644
index 0000000..64de18a
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ClassTransformer.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.ModuleVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypePath;
+
+public class ClassTransformer extends ClassVisitor {
+
+    private String className;
+
+    public ClassTransformer(final ClassWriter classVisitor) {
+        super(Opcodes.ASM8, classVisitor);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitAnnotation(descriptor, visible));
+    }
+
+    @Override
+    public FieldVisitor visitField(final int access, final String name, final String descriptor, final String signature, final Object value) {
+        return new FieldTransformer(this.api, super.visitField(access, name, descriptor, signature, value));
+    }
+
+    @Override
+    public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
+        return new MethodTransformer(this.api, super.visitMethod(access, name, descriptor, signature, exceptions));
+    }
+
+    @Override
+    public ModuleVisitor visitModule(final String name, final int access, final String version) {
+        return new ModuleTransformer(this.api, super.visitModule(name, access, version));
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
+    }
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Copy.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Copy.java
new file mode 100644
index 0000000..8b7a6cf
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Copy.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+public class Copy<T> {
+    private final T from;
+    private final T to;
+
+    public Copy(final T from, final T to) {
+        this.from = from;
+        this.to = to;
+
+    }
+
+    public <Value> Copy<T> att(final Function<T, Value> getter, final BiConsumer<T, Value> setter) {
+        final Value value = getter.apply(from);
+        if (value != null) setter.accept(to, value);
+        return this;
+    }
+
+    public static <T> Copy<T> copy(final T from, T to) {
+        return new Copy<>(from, to);
+    }
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/FieldTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/FieldTransformer.java
new file mode 100644
index 0000000..af92959
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/FieldTransformer.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.TypePath;
+
+public class FieldTransformer extends FieldVisitor {
+
+    public FieldTransformer(final int api, final FieldVisitor fieldVisitor) {
+        super(api, fieldVisitor);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitAnnotation(descriptor, visible));
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
+    }
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Is.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Is.java
new file mode 100644
index 0000000..146c8ce
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Is.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import java.io.File;
+import java.io.FileFilter;
+
+public interface Is {
+
+    class Zip implements FileFilter {
+        @Override
+        public boolean accept(final File pathname) {
+            return pathname.isFile() && accept(pathname.getName());
+        }
+
+        public static boolean accept(final String path) {
+            if (path.endsWith(".zip")) return true;
+            if (!path.endsWith("ar")) return false; // optimization
+            return path.endsWith(".jar") ||
+                    path.endsWith(".ear") ||
+                    path.endsWith(".war") ||
+                    path.endsWith(".rar");
+        }
+    }
+}
\ No newline at end of file
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/MethodTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/MethodTransformer.java
new file mode 100644
index 0000000..8666b34
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/MethodTransformer.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ConstantDynamic;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+
+public class MethodTransformer extends MethodVisitor {
+
+    public MethodTransformer(final int api, final MethodVisitor methodVisitor) {
+        super(api, methodVisitor);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        return new AnnotationTransformer(this.api, super.visitAnnotationDefault());
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitAnnotation(descriptor, visible));
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(final int parameter, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitParameterAnnotation(parameter, descriptor, visible));
+    }
+
+    @Override
+    public void visitFrame(final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack) {
+        switch (type) {
+            case -1:
+            case 0:
+//                add(bytecodeUsage, local);
+//                add(bytecodeUsage, stack);
+                break;
+            case 1:
+//                add(bytecodeUsage, local);
+                break;
+            case 2:
+                break;
+            case 3:
+                break;
+            case 4:
+//                add(bytecodeUsage, stack);
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+        super.visitFrame(type, numLocal, local, numStack, stack);
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        if (cst instanceof Integer) {
+            // ...
+        } else if (cst instanceof Float) {
+            // ...
+        } else if (cst instanceof Long) {
+            // ...
+        } else if (cst instanceof Double) {
+            // ...
+        } else if (cst instanceof String) {
+            // ...
+        } else if (cst instanceof Type) {
+            // ...
+        } else if (cst instanceof Handle) {
+            // ...
+        } else if (cst instanceof ConstantDynamic) {
+            // ...
+        } else {
+            // throw an exception
+        }
+        super.visitLdcInsn(cst);
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitInsnAnnotation(typeRef, typePath, descriptor, visible));
+    }
+
+    @Override
+    public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible));
+    }
+
+    @Override
+    public AnnotationVisitor visitLocalVariableAnnotation(final int typeRef, final TypePath typePath, final Label[] start, final Label[] end,
+                                                          final int[] index, final String descriptor, final boolean visible) {
+        return new AnnotationTransformer(this.api, super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible));
+    }
+
+
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ModuleTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ModuleTransformer.java
new file mode 100644
index 0000000..d295d28
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/ModuleTransformer.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.ModuleVisitor;
+
+public class ModuleTransformer extends ModuleVisitor {
+
+    public ModuleTransformer(final int api, final ModuleVisitor moduleVisitor) {
+        super(api, moduleVisitor);
+    }
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/SignatureTransformer.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/SignatureTransformer.java
new file mode 100644
index 0000000..3112a40
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/SignatureTransformer.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.signature.SignatureVisitor;
+
+public class SignatureTransformer extends SignatureVisitor {
+    public SignatureTransformer(final int api) {
+        super(api);
+    }
+}
diff --git a/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Transformation.java b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Transformation.java
new file mode 100644
index 0000000..8f05c8e
--- /dev/null
+++ b/tomee-patch-core/src/main/java/org/apache/tomee/patch/core/Transformation.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.tomitribe.util.IO;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+public class Transformation {
+
+    private Transformation() {
+    }
+
+    public static File transform(final File jar) throws IOException {
+        final File tempFile = File.createTempFile(jar.getName(), ".transformed");
+
+        try (final InputStream inputStream = IO.read(jar)) {
+            try (final OutputStream outputStream = IO.write(tempFile)) {
+                scanJar(inputStream, outputStream);
+                return tempFile;
+            }
+        }
+    }
+
+    private static void scanJar(final InputStream inputStream, final OutputStream outputStream) throws IOException {
+        final ZipInputStream zipInputStream = new ZipInputStream(inputStream);
+        final ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
+
+        ZipEntry oldEntry;
+        while ((oldEntry = zipInputStream.getNextEntry()) != null) {
+            // TODO: the name may be changed in transformation
+            final String path = oldEntry.getName();
+            final ZipEntry newEntry = new ZipEntry(path);
+
+            copyAttributes(oldEntry, newEntry);
+
+            zipOutputStream.putNextEntry(newEntry);
+
+            try {
+                if (path.endsWith(".class")) {
+                    scanClass(zipInputStream, zipOutputStream);
+                } else if (isZip(path)) {
+                    scanJar(zipInputStream, zipOutputStream);
+                } else {
+                    IO.copy(zipInputStream, zipOutputStream);
+                }
+            } finally {
+                zipOutputStream.closeEntry();
+            }
+        }
+        zipOutputStream.close();
+    }
+
+    private static void copyAttributes(final ZipEntry oldEntry, final ZipEntry newEntry) {
+        Copy.copy(oldEntry, newEntry)
+                .att(ZipEntry::getTime, ZipEntry::setTime)
+                .att(ZipEntry::getComment, ZipEntry::setComment)
+                .att(ZipEntry::getExtra, ZipEntry::setExtra)
+                .att(ZipEntry::getMethod, ZipEntry::setMethod)
+                .att(ZipEntry::getCreationTime, ZipEntry::setCreationTime)
+                .att(ZipEntry::getLastModifiedTime, ZipEntry::setLastModifiedTime)
+                .att(ZipEntry::getLastAccessTime, ZipEntry::setLastAccessTime);
+    }
+
+    private static boolean isZip(final String path) {
+        return Is.Zip.accept(path);
+    }
+
+    private static void scanClass(final InputStream in, final OutputStream outputStream) throws IOException {
+        final ClassWriter classWriter = new ClassWriter(Opcodes.ASM8);
+        final ClassTransformer classTransformer = new ClassTransformer(classWriter);
+        final ClassReader classReader = new ClassReader(in);
+        classReader.accept(classTransformer, 0);
+        final byte[] bytes = classWriter.toByteArray();
+        outputStream.write(bytes);
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/javax/ejb/Process.java b/tomee-patch-core/src/test/java/javax/ejb/Process.java
new file mode 100644
index 0000000..0393115
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/ejb/Process.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+//
+// This source code implements specifications defined by the Java
+// Community Process. In order to remain compliant with the specification
+// DO NOT add / change / or delete method signatures!
+//
+package javax.ejb;
+
+public class Process {
+
+    public void process(final EnterpriseBean bean) {}
+}
diff --git a/tomee-patch-core/src/test/java/javax/enterprise/context/MockScoped.java b/tomee-patch-core/src/test/java/javax/enterprise/context/MockScoped.java
new file mode 100644
index 0000000..8118d91
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/enterprise/context/MockScoped.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package javax.enterprise.context;
+
+import javax.enterprise.util.AnnotationLiteral;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@NormalScope
+@Inherited
+public @interface MockScoped {
+    public static final class Literal extends AnnotationLiteral<MockScoped> implements MockScoped {
+        public static final MockScoped.Literal INSTANCE = new MockScoped.Literal();
+        private static final long serialVersionUID = 1L;
+
+        public Literal() {
+        }
+    }
+}
diff --git a/tomee-patch-core/src/test/java/javax/jms/Consumer.java b/tomee-patch-core/src/test/java/javax/jms/Consumer.java
new file mode 100644
index 0000000..7f12a84
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/jms/Consumer.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+//
+// This source code implements specifications defined by the Java
+// Community Process. In order to remain compliant with the specification
+// DO NOT add / change / or delete method signatures!
+//
+package javax.jms;
+
+public interface Consumer<T> {
+    void accept(T t);
+}
\ No newline at end of file
diff --git a/tomee-patch-core/src/test/java/javax/jms/EnterpriseBeanConsumer.java b/tomee-patch-core/src/test/java/javax/jms/EnterpriseBeanConsumer.java
new file mode 100644
index 0000000..d7e86e4
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/jms/EnterpriseBeanConsumer.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+//
+// This source code implements specifications defined by the Java
+// Community Process. In order to remain compliant with the specification
+// DO NOT add / change / or delete method signatures!
+//
+package javax.jms;
+
+import javax.ejb.EnterpriseBean;
+
+public interface EnterpriseBeanConsumer {
+    void accept(final EnterpriseBean bean);
+}
\ No newline at end of file
diff --git a/tomee-patch-core/src/test/java/javax/persistence/EntityBean.java b/tomee-patch-core/src/test/java/javax/persistence/EntityBean.java
new file mode 100644
index 0000000..9ec4353
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/persistence/EntityBean.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+//
+// This source code implements specifications defined by the Java
+// Community Process. In order to remain compliant with the specification
+// DO NOT add / change / or delete method signatures!
+//
+package javax.persistence;
+
+import javax.ejb.EnterpriseBean;
+
+public interface EntityBean extends EnterpriseBean {
+}
diff --git a/tomee-patch-core/src/test/java/javax/persistence/Persist.java b/tomee-patch-core/src/test/java/javax/persistence/Persist.java
new file mode 100644
index 0000000..503c1f0
--- /dev/null
+++ b/tomee-patch-core/src/test/java/javax/persistence/Persist.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+//
+// This source code implements specifications defined by the Java
+// Community Process. In order to remain compliant with the specification
+// DO NOT add / change / or delete method signatures!
+//
+package javax.persistence;
+
+import javax.jms.Consumer;
+
+public class Persist<T> {
+
+    public void forEach(Consumer<? super T> action) {
+
+    }
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/AnnotationTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/AnnotationTransformerTest.java
new file mode 100644
index 0000000..1dd71b4
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/AnnotationTransformerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Test;
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.Package;
+import org.tomitribe.jkta.usage.Usage;
+
+import javax.ejb.EnterpriseBean;
+import javax.ejb.LockType;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.Path;
+
+import static org.apache.tomee.patch.core.Scan.assertUsage;
+import static org.apache.tomee.patch.core.Transform.usage;
+
+public class AnnotationTransformerTest {
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_primitive() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(length = 4)
+            public void get() {
+            }
+        });
+
+        assertUsage(usage);
+
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_String() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(name = "javax.ejb.EnterpriseBean")
+            public void m1() {
+            }
+
+            @Data(name = "javax/ejb/EnterpriseBean")
+            public void m2() {
+            }
+
+            @Data(name = "Ljavax/ejb/EnterpriseBean")
+            public void m3() {
+            }
+        });
+
+        assertUsage(usage);
+
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_Class() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(type = EnterpriseBean.class)
+            public void get() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitEnum() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(lock = LockType.WRITE)
+            public void get() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(path = @Path("/foo"))
+            public void get() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_WS_RS);
+
+    }
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            @ArrayData(data = @Data(path = @Path("/foo")))
+            public void m() {
+            }
+
+            @ArrayData(data = @Data(type = HttpServlet.class))
+            public void m2() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_WS_RS, Package.JAVAX_SERVLET);
+
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitArray() {
+        final Usage<Jar> usage = usage(new Object() {
+            @ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)})
+            public void m() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_WS_RS, Package.JAVAX_SERVLET);
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ArrayData.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ArrayData.java
new file mode 100644
index 0000000..fe1a574
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ArrayData.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.TYPE_PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+@Target({TYPE,
+        FIELD,
+        METHOD,
+        PARAMETER,
+        CONSTRUCTOR,
+        LOCAL_VARIABLE,
+        ANNOTATION_TYPE,
+        PACKAGE,
+        TYPE_PARAMETER,
+        TYPE_USE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ArrayData {
+    Data[] data() default {};
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Asmifier.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Asmifier.java
new file mode 100644
index 0000000..1c7a26a
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Asmifier.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.util.ASMifier;
+import org.objectweb.asm.util.TraceClassVisitor;
+import org.tomitribe.jkta.usage.Is;
+import org.tomitribe.util.Hex;
+import org.tomitribe.util.IO;
+import org.tomitribe.util.PrintString;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.net.URL;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class Asmifier {
+
+    private static final AtomicReference<File> tmpDir = new AtomicReference<File>(null);
+
+    static {
+        try {
+            final File asmifier = File.createTempFile("Asmifier", null);
+            tmpDir.set(asmifier.getParentFile());
+            if (!asmifier.delete()) {
+                asmifier.deleteOnExit();
+            }
+        } catch (final IOException e) {
+            throw new RuntimeException("Asmifier failed to locate a temporary directory");
+        }
+    }
+
+    public static void hex(final ClassLoader classLoader, final String className) throws IOException {
+        final String internalName = className.replace('.', '/') + ".class";
+        final URL resource = classLoader.getResource(internalName);
+
+        if (null == resource) {
+            throw new IOException("Failed to find resource: " + internalName);
+        }
+        System.out.println(className);
+        final InputStream inputStream = resource.openStream();
+        byte[] bytes = IO.readBytes(inputStream);
+        for (final byte b : bytes) {
+            System.out.printf("%-2s %-2s%n", Hex.toString(new byte[]{b}), (char) b);
+        }
+    }
+
+    public static void print(final ClassLoader classLoader, final String className) throws IOException {
+        final String internalName = className.replace('.', '/') + ".class";
+        final URL resource = classLoader.getResource(internalName);
+
+        if (null == resource) {
+            throw new IOException("Failed to find resource: " + internalName);
+        }
+
+        final ClassReader reader = new ClassReader(resource.openStream());
+
+
+        final File file = new File("/tmp/" + className);
+
+        write(reader, file);
+    }
+
+    private static void write(final ClassReader reader, final File file) throws IOException {
+        final OutputStream write = IO.write(file);
+        write(reader, write);
+    }
+
+    public static void write(final ClassReader reader, final OutputStream write) throws IOException {
+        final TraceClassVisitor visitor = new TraceClassVisitor(null, new ASMifier(), new PrintWriter(write));
+        reader.accept(visitor, ClassReader.SKIP_DEBUG);
+        write.close();
+    }
+
+    public static void asmify(final Class clazz, final String suffix) throws IOException {
+        asmify(clazz.getName(), Bytecode.readClassFile(clazz.getClassLoader(), clazz), suffix);
+    }
+
+    public static void asmify(final ClassLoader loader, final String className, final String suffix) throws IOException {
+        asmify(className, Bytecode.readClassFile(loader, className), suffix);
+    }
+
+    public static void asmify(final String className, final byte[] bytes, final String suffix) throws IOException {
+        final ClassReader reader = new ClassReader(bytes);
+        final File file = new File(tmpDir.get(), className + "." + suffix);
+
+        write(reader, file);
+    }
+
+    public static String asmify(final byte[] actualBytes) throws IOException {
+        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        write(new ClassReader(actualBytes), byteArrayOutputStream);
+        return new String(byteArrayOutputStream.toByteArray());
+    }
+
+    public static String asmifyJar(final File jar) throws IOException {
+        final PrintString printString = new PrintString();
+        final InputStream read = IO.read(jar);
+        asmifyJar(read, printString);
+        return printString.toString();
+    }
+
+    private static void asmifyJar(final InputStream inputStream, final PrintStream out) throws IOException {
+        final ZipInputStream zipInputStream = new ZipInputStream(inputStream);
+
+        ZipEntry entry;
+        while ((entry = zipInputStream.getNextEntry()) != null) {
+
+            entryMetadata(entry, out);
+
+            final String path = entry.getName();
+
+            if (path.endsWith(".class")) {
+                asmifyClass(zipInputStream, out);
+            } else if (isZip(path)) {
+                asmifyJar(zipInputStream, out);
+            } else {
+                IO.copy(zipInputStream, out);
+            }
+        }
+    }
+
+    private static void entryMetadata(final ZipEntry entry, final PrintStream out) {
+        out.printf("%n-----------------------------------------------%n");
+        out.printf("name: %s%n", entry.getName());
+        out.printf("----%n");
+    }
+
+    private static void asmifyClass(final InputStream inputStream, final PrintStream out) {
+        try {
+            final byte[] bytes = IO.readBytes(inputStream);
+            final String asmify = Asmifier.asmify(bytes);
+            out.println(asmify);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private static boolean isZip(final String path) {
+        return Is.Zip.accept(path);
+    }
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Bytecode.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Bytecode.java
new file mode 100644
index 0000000..445d528
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Bytecode.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.tomitribe.util.IO;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class Bytecode {
+
+    private Bytecode() {
+        // no-op
+    }
+
+    public static byte[] readClassFile(final Class clazz) throws IOException {
+        ClassLoader classLoader = clazz.getClassLoader();
+        if (classLoader == null) {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        }
+        return readClassFile(classLoader, clazz);
+    }
+
+    public static byte[] readClassFile(final ClassLoader classLoader, final Class clazz) throws IOException {
+        final String internalName = clazz.getName().replace('.', '/') + ".class";
+        final URL resource = classLoader.getResource(internalName);
+        return IO.readBytes(resource);
+    }
+
+    public static byte[] readClassFile(final ClassLoader classLoader, final String className) throws IOException {
+        final String internalName = className.replace('.', '/') + ".class";
+        final URL resource = classLoader.getResource(internalName);
+        return IO.readBytes(resource);
+    }
+
+    public static void read(final byte[] originalBytes, final ClassVisitor classAdapter) {
+        if (originalBytes == null) throw new IllegalStateException("bytecode array is null");
+        final ClassReader cr = new ClassReader(originalBytes);
+        cr.accept(classAdapter, ClassReader.EXPAND_FRAMES);
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ClassTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ClassTransformerTest.java
new file mode 100644
index 0000000..c0ac8f4
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ClassTransformerTest.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.Package;
+import org.tomitribe.jkta.usage.Usage;
+
+import javax.ejb.EJBException;
+import javax.ejb.EnterpriseBean;
+import javax.ejb.SessionBean;
+import javax.enterprise.context.MockScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.spi.Bean;
+import javax.persistence.Persistence;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.Path;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.util.Set;
+
+import static org.apache.tomee.patch.core.Scan.assertUsage;
+import static org.apache.tomee.patch.core.Transform.usage;
+
+public class ClassTransformerTest {
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_Negative() {
+        final Usage<Jar> usage = usage(VisitNegative.class);
+
+        assertUsage(usage);
+    }
+
+    public static class Parent {
+    }
+
+    public static interface Contract {
+    }
+
+    public static class VisitNegative extends Parent implements Serializable, Contract {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_SuperClass() {
+        final Usage<Jar> usage = usage(HasSuper.class);
+
+        assertUsage(usage, Package.JAVAX_SERVLET, Package.JAVAX_SERVLET);
+
+    }
+
+    public static class HasSuper extends HttpServlet implements Serializable, Contract {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_Interfaces() {
+        final Usage<Jar> usage = usage(HasInterface.class);
+
+        assertUsage(usage, Package.JAVAX_EJB);
+
+    }
+
+    public static class HasInterface implements Serializable, EnterpriseBean, Contract {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_Signature() {
+        final Usage<Jar> usage = usage(HasSignature.class);
+
+        assertUsage(usage, Package.JAVAX_EJB);
+
+    }
+
+    public static class HasSignature<T extends SessionBean> {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_Signature2() {
+        final Usage<Jar> usage = usage(HasSignature2.class);
+
+        assertUsage(usage, Package.JAVAX_EJB);
+
+    }
+
+    public static interface HasSignature2 extends Set<SessionBean> {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visit_All() {
+        final Usage<Jar> usage = usage(HasAll.class);
+
+        assertUsage(usage,
+                Package.JAVAX_EJB,
+                Package.JAVAX_SERVLET,
+                Package.JAVAX_SERVLET,
+                Package.JAVAX_PERSISTENCE
+        );
+
+    }
+
+    public static interface Generic<S> {
+    }
+
+    public static class HasAll extends HttpServlet implements Serializable, EnterpriseBean, Generic<Persistence> {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation() {
+        final Usage<Jar> usage = usage(HasAnnotation.class);
+
+        assertUsage(usage, Package.JAVAX_ENTERPRISE);
+    }
+
+    @RequestScoped
+    public static class HasAnnotation {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation_Deep() {
+        final Usage<Jar> usage = usage(HasAnnotationData.class);
+
+        assertUsage(usage, Package.JAVAX_WS_RS, Package.JAVAX_SERVLET);
+    }
+
+    @ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)})
+    public static class HasAnnotationData {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeAnnotation() {
+        final Usage<Jar> usage = usage(HasTypeAnnotation.class);
+
+        assertUsage(usage, Package.JAVAX_ENTERPRISE);
+    }
+
+    public static class HasTypeAnnotation implements Generic<@MockScoped Reference> {
+    }
+
+    // ------------------------------------------------------
+
+    @Ignore
+    @Test
+    public void visitTypeAnnotation_Deep_PossibleAsmBug() {
+        final Usage<Jar> usage = usage(HasTypeAnnotationDeep.class);
+
+        assertUsage(usage, Package.JAVAX_WS_RS, Package.JAVAX_SERVLET);
+    }
+
+    public static class HasTypeAnnotationDeep implements Generic<@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) Reference> {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitField() {
+        final Usage<Jar> usage = usage(new Object() {
+            SessionBean sb;
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitField_Signature1() {
+        final Usage<Jar> usage = usage(new Object() {
+            final Reference<SessionBean> sb = null;
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    /**
+     * The "Bean" type will show up both in ASM's field descriptor
+     * and in the generic signature string.  We should only count
+     * one of them.
+     */
+    @Test
+    public void visitField_SignatureNoDuplicate() {
+        final Usage<Jar> usage = usage(new Object() {
+            Bean<SessionBean> bean;
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB, Package.JAVAX_ENTERPRISE);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethod_Return() {
+        final Usage<Jar> usage = usage(new Object() {
+            public SessionBean get() {
+                return null;
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethod_ReturnGeneric() {
+        final Usage<Jar> usage = usage(new Object() {
+            public Generic<SessionBean> get() {
+                return null;
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethod_Parameter() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void get(SessionBean sb) {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethod_ParameterGeneric() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void get(Object o, Generic<SessionBean> sb, int i) {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethod_Throws() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void get(Serializable s) throws EJBException, IOException {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Data.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Data.java
new file mode 100644
index 0000000..40ba02f
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Data.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import javax.ejb.LockType;
+import javax.ws.rs.Path;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Data {
+    int length() default 2;
+
+    String name() default "";
+
+    LockType lock() default LockType.READ;
+
+    Path path() default @Path("/");
+
+    Class type() default Object.class;
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/FieldTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/FieldTransformerTest.java
new file mode 100644
index 0000000..ebf85ed
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/FieldTransformerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.Package;
+import org.tomitribe.jkta.usage.Usage;
+
+import javax.enterprise.context.MockScoped;
+import javax.persistence.Id;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.Path;
+import java.util.Set;
+
+import static org.apache.tomee.patch.core.Scan.assertUsage;
+import static org.apache.tomee.patch.core.Transform.usage;
+
+public class FieldTransformerTest {
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Id
+            private Object o;
+        });
+
+        assertUsage(usage, Package.JAVAX_PERSISTENCE);
+    }
+
+    // ------------------------------------------------------
+
+    @Ignore
+    @Test
+    public void visitAnnotation_Deep_PossibleAsmBug() {
+        final Usage<Jar> usage = usage(new Object() {
+            @ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)})
+            private Object o;
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET, Package.JAVAX_WS_RS);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Data(type = HttpServlet.class)
+            private Object o;
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            Set<@MockScoped Long> set;
+        });
+
+        assertUsage(usage, Package.JAVAX_ENTERPRISE);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            Set<@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) Long> set;
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET, Package.JAVAX_WS_RS);
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/MethodTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/MethodTransformerTest.java
new file mode 100644
index 0000000..914d1f7
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/MethodTransformerTest.java
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.tomitribe.jkta.usage.BytecodeUsage;
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.MethodScanner;
+import org.tomitribe.jkta.usage.Package;
+import org.tomitribe.jkta.usage.Usage;
+
+import javax.ejb.EnterpriseBean;
+import javax.ejb.Process;
+import javax.ejb.Schedule;
+import javax.ejb.ScheduleExpression;
+import javax.ejb.SessionBean;
+import javax.enterprise.context.MockScoped;
+import javax.jms.EnterpriseBeanConsumer;
+import javax.persistence.EntityBean;
+import javax.persistence.Persist;
+import javax.persistence.PersistenceException;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Cookie;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.tomee.patch.core.Scan.assertUsage;
+import static org.apache.tomee.patch.core.Transform.usage;
+
+public class MethodTransformerTest {
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            @Schedule
+            public void get() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            @ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)})
+            public void get() {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET, Package.JAVAX_WS_RS);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            public <@MockScoped V> V get() {
+                return null;
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_ENTERPRISE);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            public <@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) V> V get() {
+                return null;
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET, Package.JAVAX_WS_RS);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitParameterAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(@PathParam("id") int id) {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_WS_RS);
+    }
+
+    // ------------------------------------------------------
+
+    @Ignore
+    @Test
+    public void visitParameterAnnotation_Deep_PotentialAsmBug() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) int id) {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_WS_RS, Package.JAVAX_SERVLET);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitParameterAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(@Data(type = HttpServlet.class) int id) {
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_SERVLET);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitFrame() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(Object o) {
+                EnterpriseBean bean = (SessionBean) o;
+                try {
+                    m(bean);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTypeInsn() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(Object o) {
+                final Object bean = (SessionBean) o;
+                System.out.println(bean);
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitFieldInsn() {
+        final Usage<Jar> usage = usage(new Object() {
+            final SessionBean bean = null;
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethodInsn() {
+        final Usage<Jar> usage = usage(new Object() {
+
+            public void m() {
+                new ScheduleExpression();
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_EJB, Package.JAVAX_EJB);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMethodInsn_Descriptor() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m(final EnterpriseBeanConsumer consumer, final EntityBean bean) {
+                consumer.accept(bean);
+            }
+        });
+
+        assertUsage(usage, Package.JAVAX_JMS, Package.JAVAX_JMS, Package.JAVAX_EJB, Package.JAVAX_PERSISTENCE);
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitInvokeDynamicInsn_Direct() {
+        final Usage usage = new Usage();
+        final MethodScanner methodScanner = new MethodScanner(Opcodes.ASM8, new BytecodeUsage(usage, Opcodes.ASM8));
+        methodScanner.visitInvokeDynamicInsn(
+                "accept",
+                "(Ljavax/ejb/Process;)Ljavax/jms/Consumer;",
+                new Handle(Opcodes.H_INVOKESTATIC,
+                        "java/lang/invoke/LambdaMetafactory",
+                        "metafactory",
+                        "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
+                        false),
+                new Object[]{
+                        Type.getType("(Ljava/lang/Object;)V"),
+                        new Handle(Opcodes.H_INVOKEVIRTUAL,
+                                "javax/ejb/Process",
+                                "process",
+                                "(Ljavax/ejb/EnterpriseBean;)V",
+                                false),
+                        Type.getType("(Ljavax/ejb/EnterpriseBean;)V")
+                }
+        );
+
+        assertUsage(usage,
+                Package.JAVAX_EJB,
+                Package.JAVAX_EJB,
+                Package.JAVAX_EJB,
+                Package.JAVAX_EJB,
+                Package.JAVAX_JMS
+        );
+
+    }
+
+    @Test
+    public void visitInvokeDynamicInsn() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void invokedynamic(Persist<EnterpriseBean> persist, Process process) {
+                persist.forEach(process::process);
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_PERSISTENCE, // visitMethod
+                Package.JAVAX_EJB, // visitMethod
+                Package.JAVAX_EJB, // visitMethod
+                Package.JAVAX_EJB, // visitInvokeDynamicInsn
+                Package.JAVAX_EJB, // visitInvokeDynamicInsn
+                Package.JAVAX_EJB, // visitInvokeDynamicInsn
+                Package.JAVAX_EJB, // visitInvokeDynamicInsn
+                Package.JAVAX_JMS, // visitInvokeDynamicInsn
+                Package.JAVAX_PERSISTENCE, // visitMethodInsn
+                Package.JAVAX_JMS // visitMethodInsn
+        );
+
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitLdcInsn_Type() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                System.out.println(GET.class);
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_WS_RS
+        );
+    }
+
+    // ------------------------------------------------------
+
+    /**
+     * TODO This is a stub.  Real test needed.
+     */
+    @Ignore("https://github.com/tomitribe/jkta/issues/2")
+    @Test
+    public void visitLdcInsn_Handle() {
+    }
+
+    // ------------------------------------------------------
+
+
+    /**
+     * TODO This is a stub.  Real test needed.
+     */
+    @Ignore("https://github.com/tomitribe/jkta/issues/3")
+    @Test
+    public void visitLdcInsn_ConstantDynamic() {
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitMultiANewArrayInsn() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                final Cookie[][][] cookies = new Cookie[4][8][16];
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_WS_RS
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitInsnAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                final List list = (@MockScoped ArrayList) null;
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_ENTERPRISE
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitInsnAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                final List list = (@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) ArrayList) null;
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_SERVLET,
+                Package.JAVAX_WS_RS
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTryCatchBlock() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                try {
+                    System.out.println();
+                } catch (PersistenceException e) {
+                    System.out.println();
+                }
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_PERSISTENCE, // visitTryCatchBlock
+                Package.JAVAX_PERSISTENCE // visitFrame
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTryCatchAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                try {
+                    System.out.println();
+                } catch (@MockScoped PersistenceException e) {
+                    System.out.println();
+                }
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_ENTERPRISE, // visitTryCatchBlock
+                Package.JAVAX_PERSISTENCE, // visitTryCatchBlock
+                Package.JAVAX_PERSISTENCE // visitFrame
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitTryCatchAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                try {
+                    System.out.println();
+                } catch (@ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) PersistenceException e) {
+                    System.out.println();
+                }
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_SERVLET,
+                Package.JAVAX_WS_RS,
+                Package.JAVAX_PERSISTENCE,
+                Package.JAVAX_PERSISTENCE
+        );
+    }
+
+    // ------------------------------------------------------
+
+    @Test
+    public void visitLocalVariableAnnotation() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                @MockScoped long e = System.nanoTime();
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_ENTERPRISE
+        );
+    }
+    // ------------------------------------------------------
+
+    @Test
+    public void visitLocalVariableAnnotation_Deep() {
+        final Usage<Jar> usage = usage(new Object() {
+            public void m() {
+                @ArrayData(data = {@Data(path = @Path("/foo")), @Data(type = HttpServlet.class)}) long e = System.nanoTime();
+            }
+        });
+
+        assertUsage(usage,
+                Package.JAVAX_SERVLET,
+                Package.JAVAX_WS_RS
+        );
+    }
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ModuleTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ModuleTransformerTest.java
new file mode 100644
index 0000000..6df77e1
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/ModuleTransformerTest.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+public class ModuleTransformerTest {
+
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Scan.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Scan.java
new file mode 100644
index 0000000..db482b2
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Scan.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.JarUsage;
+import org.tomitribe.jkta.usage.Package;
+import org.tomitribe.jkta.usage.Usage;
+import org.tomitribe.util.Archive;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+
+import static org.junit.Assert.assertEquals;
+
+public class Scan {
+    private Scan() {
+    }
+
+    public static Usage<Jar> usage(final Object o) {
+        return usage(o.getClass());
+    }
+
+    public static Usage<Jar> usage(final Class<?> aClass) {
+        try {
+//            final ClassLoader loader = aClass.getClassLoader();
+//            System.out.println(Asmifier.asmify(Bytecode.readClassFile(loader, aClass)));
+//            System.out.println();
+            final File jar = Archive.archive().add(aClass).toJar();
+            return JarUsage.of(jar);
+        } catch (IOException | NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void assertUsage(final Usage<Jar> usage, final Package... expectedUses) {
+        int javax = 0;
+        int jakarta = 0;
+        int[] uses = new int[Package.values().length];
+
+        for (final Package use : expectedUses) {
+            uses[use.ordinal()]++;
+            if (use.getName().startsWith("javax.")) javax++;
+            if (use.getName().startsWith("jakarta.")) jakarta++;
+        }
+
+        for (int i = 0; i < uses.length; i++) {
+            final Package pkg = Package.values()[i];
+            assertEquals(pkg.getName(), uses[i], usage.get(pkg));
+        }
+
+        assertEquals(javax, usage.getJavax());
+        assertEquals(jakarta, usage.getJakarta());
+    }
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/SignatureTransformerTest.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/SignatureTransformerTest.java
new file mode 100644
index 0000000..7a62a79
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/SignatureTransformerTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Test;
+
+public class SignatureTransformerTest {
+
+    @Test
+    public void visitArrayType() {
+    }
+
+    @Test
+    public void visitClassType() {
+    }
+
+    @Test
+    public void visitInnerClassType() {
+    }
+
+    @Test
+    public void visitTypeArgument() {
+    }
+}
diff --git a/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Transform.java b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Transform.java
new file mode 100644
index 0000000..f3fdfa3
--- /dev/null
+++ b/tomee-patch-core/src/test/java/org/apache/tomee/patch/core/Transform.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.core;
+
+import org.junit.Assert;
+import org.tomitribe.jkta.usage.Jar;
+import org.tomitribe.jkta.usage.JarUsage;
+import org.tomitribe.jkta.usage.Usage;
+import org.tomitribe.util.Archive;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.util.stream.Stream;
+
+public class Transform {
+    private Transform() {
+    }
+
+    public static Usage<Jar> usage(final Object o) {
+        return usage(o.getClass());
+    }
+
+    public static Usage<Jar> usage(final Class<?> aClass) {
+        try {
+            final File jar = new TestArchive().add(aClass).toJar();
+
+            final Usage<Jar> usageBefore = JarUsage.of(jar);
+
+            final File transformed = Transformation.transform(jar);
+
+            final String contentsBefore = Asmifier.asmifyJar(jar);
+            final String contentsAfter = Asmifier.asmifyJar(transformed);
+
+            Assert.assertEquals(contentsBefore, contentsAfter);
+
+            return JarUsage.of(transformed);
+        } catch (IOException | NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static class TestArchive extends Archive {
+        public Archive add(final Class<?> clazz) {
+            try {
+                final String name = clazz.getName().replace('.', '/') + ".class";
+
+                final URL resource = this.getClass().getClassLoader().getResource(name);
+                if (resource == null) throw new IllegalStateException("Cannot find class file for " + clazz.getName());
+                add(name, resource);
+
+                // Add any anonymous nested classes
+                Stream.of(clazz.getDeclaredClasses())
+                        .filter(Class::isAnonymousClass)
+                        .forEach(this::add);
+
+                return this;
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/tomee-patch-plugin/pom.xml b/tomee-patch-plugin/pom.xml
index 5ed2fb9..f68116a 100644
--- a/tomee-patch-plugin/pom.xml
+++ b/tomee-patch-plugin/pom.xml
@@ -31,5 +31,93 @@
   <artifactId>tomee-patch-plugin</artifactId>
   <packaging>maven-plugin</packaging>
 
+  <properties>
+    <maven.version>3.2.2</maven.version>
+    <maven-plugin.prefix>transform</maven-plugin.prefix>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <version>3.3</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <version>3.0.17</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-model</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-cli</groupId>
+      <artifactId>commons-cli</artifactId>
+      <version>1.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-compat</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+
+    <!-- TESTS -->
+    <dependency>
+      <groupId>org.apache.maven.plugin-testing</groupId>
+      <artifactId>maven-plugin-testing-harness</artifactId>
+      <version>3.2.0</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-aether-provider</artifactId>
+      <version>${maven.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.jboss.shrinkwrap</groupId>
+      <artifactId>shrinkwrap-api</artifactId>
+      <version>1.2.6</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.shrinkwrap</groupId>
+      <artifactId>shrinkwrap-impl-base</artifactId>
+      <version>1.2.6</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax</groupId>
+      <artifactId>javaee-api</artifactId>
+      <version>8.0.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
 
diff --git a/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/TransformMojo.java b/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/TransformMojo.java
new file mode 100644
index 0000000..27c06ad
--- /dev/null
+++ b/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/TransformMojo.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomee.patch.plugin;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+@Mojo(name = "run", requiresDependencyResolution = ResolutionScope.RUNTIME_PLUS_SYSTEM, defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true)
+public class TransformMojo extends AbstractMojo {
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+
+    }
+}