diff --git a/core-java-modules/core-java-15/pom.xml b/core-java-modules/core-java-15/pom.xml index 8cb6c2410d..a4923054ac 100644 --- a/core-java-modules/core-java-15/pom.xml +++ b/core-java-modules/core-java-15/pom.xml @@ -21,6 +21,11 @@ commons-lang3 ${commons-lang3.version} + + commons-io + commons-io + ${commons-io.version} + diff --git a/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java b/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java new file mode 100644 index 0000000000..d90e512b39 --- /dev/null +++ b/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java @@ -0,0 +1,7 @@ +package com.baeldung.hidden.classes; + +public class HiddenClass { + public String convertToUpperCase(String s) { + return s.toUpperCase(); + } +} diff --git a/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java b/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java new file mode 100644 index 0000000000..ac6670b208 --- /dev/null +++ b/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java @@ -0,0 +1,60 @@ +package com.baeldung.hidden.classes; + + +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup.ClassOption; +import java.lang.reflect.Method; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HiddenClassUnitTest { + + private static final Logger LOG = LoggerFactory.getLogger(HiddenClassUnitTest.class); + + @Test + void whenInvokeMethodOnHiddenClass_thenSuccess() { + // Initiate lookup class + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + // Create a byte code of a class + + Class clazz = HiddenClass.class; + String className = clazz.getName(); + String classAsPath = className.replace('.', '/') + ".class"; + InputStream stream = clazz.getClassLoader() + .getResourceAsStream(classAsPath); + + try { + // Define hidden class with byte code + Class hiddenClass = lookup.defineHiddenClass(IOUtils.toByteArray(stream), true, ClassOption.NESTMATE) + .lookupClass(); + Object hiddenClassObject = hiddenClass.getConstructor() + .newInstance(); + + Method method = hiddenClassObject.getClass() + .getDeclaredMethod("convertToUpperCase", String.class); + + Assertions.assertEquals(true, hiddenClass.isHidden()); + + Assertions.assertEquals("HELLO", method.invoke(hiddenClassObject, "Hello")); + + Assertions.assertEquals(this.getClass() + .getClassLoader(), hiddenClass.getClassLoader()); + + Assertions.assertEquals(null, hiddenClass.getCanonicalName()); + + Assertions.assertThrows(ClassNotFoundException.class, () -> Class.forName(hiddenClass.getName())); + + Assertions.assertThrows(ClassNotFoundException.class, () -> lookup.findClass(hiddenClass.getName())); + + } catch (Exception e) { + LOG.error("Couldn't instantiate hidden class" + e); + } + } + +}