diff --git a/classloader/build-include-jdk-misc b/classloader/build-include-jdk-misc
new file mode 100644
index 00000000..e69de29b
diff --git a/classloader/build-release-8 b/classloader/build-release-8
new file mode 100644
index 00000000..e69de29b
diff --git a/classloader/build-test-java8 b/classloader/build-test-java8
new file mode 100644
index 00000000..e69de29b
diff --git a/classloader/build-test-java9 b/classloader/build-test-java9
new file mode 100644
index 00000000..e69de29b
diff --git a/classloader/pom.xml b/classloader/pom.xml
new file mode 100644
index 00000000..98a651b2
--- /dev/null
+++ b/classloader/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+
+ io.smallrye.common
+ smallrye-common-parent
+ 1.1.1-SNAPSHOT
+
+
+ smallrye-common-classloader
+ SmallRye Common: Classloader
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+
+
+ org.ow2.asm
+ asm
+ test
+
+
+
+
+
+ coverage
+
+ @{jacocoArgLine}
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ report
+ verify
+
+ report
+
+
+
+
+
+
+
+
+
+
diff --git a/classloader/src/main/java/io/smallrye/common/classloader/ClassDefiner.java b/classloader/src/main/java/io/smallrye/common/classloader/ClassDefiner.java
new file mode 100644
index 00000000..bbc8b0a2
--- /dev/null
+++ b/classloader/src/main/java/io/smallrye/common/classloader/ClassDefiner.java
@@ -0,0 +1,46 @@
+package io.smallrye.common.classloader;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.misc.Unsafe;
+
+public class ClassDefiner {
+ private static final Unsafe unsafe;
+
+ static {
+ unsafe = AccessController.doPrivileged(new PrivilegedAction() {
+ public Unsafe run() {
+ try {
+ final Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+ return (Unsafe) field.get(null);
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ } catch (NoSuchFieldException e) {
+ throw new NoSuchFieldError(e.getMessage());
+ }
+ }
+ });
+ }
+
+ public static Class> defineClass(MethodHandles.Lookup lookup, Class> parent, String className, byte[] classBytes) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(DefineClassPermission.getInstance());
+ }
+
+ // The className for Unsafe.defineClass must match the one in classBytes, so we can try to verify the package
+ // with the parent
+ String parentPkg = parent.getPackage().getName();
+ String classPkg = className.substring(0, className.lastIndexOf('.'));
+
+ if (!parentPkg.equals(classPkg)) {
+ throw new IllegalArgumentException("Class not in same package as lookup class");
+ }
+
+ return unsafe.defineClass(className, classBytes, 0, classBytes.length, parent.getClassLoader(), null);
+ }
+}
diff --git a/classloader/src/main/java/io/smallrye/common/classloader/DefineClassPermission.java b/classloader/src/main/java/io/smallrye/common/classloader/DefineClassPermission.java
new file mode 100644
index 00000000..5a44fd47
--- /dev/null
+++ b/classloader/src/main/java/io/smallrye/common/classloader/DefineClassPermission.java
@@ -0,0 +1,40 @@
+package io.smallrye.common.classloader;
+
+import java.security.Permission;
+
+public class DefineClassPermission extends Permission {
+ private static final long serialVersionUID = 142067672163413424L;
+ private static final DefineClassPermission INSTANCE = new DefineClassPermission();
+
+ public DefineClassPermission() {
+ super("");
+ }
+
+ public DefineClassPermission(final String name, final String actions) {
+ this();
+ }
+
+ public static DefineClassPermission getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean implies(final Permission permission) {
+ return permission != null && permission.getClass() == this.getClass();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof DefineClassPermission;
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ @Override
+ public String getActions() {
+ return "";
+ }
+}
diff --git a/classloader/src/main/java9/io/smallrye/common/classloader/ClassDefiner.java b/classloader/src/main/java9/io/smallrye/common/classloader/ClassDefiner.java
new file mode 100644
index 00000000..0e5fe270
--- /dev/null
+++ b/classloader/src/main/java9/io/smallrye/common/classloader/ClassDefiner.java
@@ -0,0 +1,26 @@
+package io.smallrye.common.classloader;
+
+import java.lang.invoke.MethodHandles;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class ClassDefiner {
+ public static Class> defineClass(MethodHandles.Lookup lookup, Class> parent, String className, byte[] classBytes) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(DefineClassPermission.getInstance());
+ }
+
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ @Override
+ public Class> run() {
+ try {
+ MethodHandles.Lookup privateLookupIn = MethodHandles.privateLookupIn(parent, lookup);
+ return privateLookupIn.defineClass(classBytes);
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ }
+ }
+ });
+ }
+}
diff --git a/classloader/src/test/java/io/smallrye/common/classloader/ClassDefinerTest.java b/classloader/src/test/java/io/smallrye/common/classloader/ClassDefinerTest.java
new file mode 100644
index 00000000..feeb47c3
--- /dev/null
+++ b/classloader/src/test/java/io/smallrye/common/classloader/ClassDefinerTest.java
@@ -0,0 +1,65 @@
+package io.smallrye.common.classloader;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.V1_8;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+
+class ClassDefinerTest {
+ @Test
+ void defineClass() throws Exception {
+ Class> helloClass = ClassDefiner.defineClass(MethodHandles.lookup(), ClassDefinerTest.class,
+ "io.smallrye.common.classloader.TestHello",
+ getHelloClass("io.smallrye.common.classloader.TestHello"));
+ assertNotNull(helloClass);
+
+ Object hello = helloClass.getDeclaredConstructor().newInstance();
+ Method helloMethod = helloClass.getDeclaredMethod("hello");
+ assertEquals("hello", helloMethod.invoke(hello));
+ }
+
+ @Test
+ void notAllowDifferentPackages() {
+ assertThrows(IllegalArgumentException.class,
+ () -> ClassDefiner.defineClass(MethodHandles.lookup(), ClassDefinerTest.class,
+ "io.smallrye.common.something.TestHello",
+ getHelloClass("io.smallrye.common.something.TestHello")));
+ }
+
+ private byte[] getHelloClass(final String name) {
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, "java/lang/Object", null);
+
+ {
+ MethodVisitor methodVisitor = writer.visitMethod(ACC_PUBLIC, "", "()V", null, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+
+ {
+ MethodVisitor methodVisitor = writer.visitMethod(ACC_PUBLIC, "hello", "()Ljava/lang/String;", null, null);
+ methodVisitor.visitLdcInsn("hello");
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ writer.visitEnd();
+ }
+
+ return writer.toByteArray();
+ }
+}
diff --git a/pom.xml b/pom.xml
index 673dc4c7..b990f0aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,7 @@
annotation
+ classloader
constraint
cpu
expression
@@ -78,7 +79,12 @@
pom
import
-
+
+ org.ow2.asm
+ asm
+ 7.0
+ test
+