Skip to content

Commit 9448ff6

Browse files
authored
[BAEL-5077] Hidden Classes (eugenp#11663)
1 parent a2779d4 commit 9448ff6

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

core-java-modules/core-java-15/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
<artifactId>commons-lang3</artifactId>
2222
<version>${commons-lang3.version}</version>
2323
</dependency>
24+
<dependency>
25+
<groupId>commons-io</groupId>
26+
<artifactId>commons-io</artifactId>
27+
<version>${commons-io.version}</version>
28+
</dependency>
2429
</dependencies>
2530

2631
<build>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.hidden.classes;
2+
3+
public class HiddenClass {
4+
public String convertToUpperCase(String s) {
5+
return s.toUpperCase();
6+
}
7+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.baeldung.hidden.classes;
2+
3+
4+
import java.io.InputStream;
5+
import java.lang.invoke.MethodHandles;
6+
import java.lang.invoke.MethodHandles.Lookup.ClassOption;
7+
import java.lang.reflect.Method;
8+
9+
import org.apache.commons.io.IOUtils;
10+
import org.junit.jupiter.api.Assertions;
11+
import org.junit.jupiter.api.Test;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
public class HiddenClassUnitTest {
16+
17+
private static final Logger LOG = LoggerFactory.getLogger(HiddenClassUnitTest.class);
18+
19+
@Test
20+
void whenInvokeMethodOnHiddenClass_thenSuccess() {
21+
// Initiate lookup class
22+
MethodHandles.Lookup lookup = MethodHandles.lookup();
23+
24+
// Create a byte code of a class
25+
26+
Class<?> clazz = HiddenClass.class;
27+
String className = clazz.getName();
28+
String classAsPath = className.replace('.', '/') + ".class";
29+
InputStream stream = clazz.getClassLoader()
30+
.getResourceAsStream(classAsPath);
31+
32+
try {
33+
// Define hidden class with byte code
34+
Class<?> hiddenClass = lookup.defineHiddenClass(IOUtils.toByteArray(stream), true, ClassOption.NESTMATE)
35+
.lookupClass();
36+
Object hiddenClassObject = hiddenClass.getConstructor()
37+
.newInstance();
38+
39+
Method method = hiddenClassObject.getClass()
40+
.getDeclaredMethod("convertToUpperCase", String.class);
41+
42+
Assertions.assertEquals(true, hiddenClass.isHidden());
43+
44+
Assertions.assertEquals("HELLO", method.invoke(hiddenClassObject, "Hello"));
45+
46+
Assertions.assertEquals(this.getClass()
47+
.getClassLoader(), hiddenClass.getClassLoader());
48+
49+
Assertions.assertEquals(null, hiddenClass.getCanonicalName());
50+
51+
Assertions.assertThrows(ClassNotFoundException.class, () -> Class.forName(hiddenClass.getName()));
52+
53+
Assertions.assertThrows(ClassNotFoundException.class, () -> lookup.findClass(hiddenClass.getName()));
54+
55+
} catch (Exception e) {
56+
LOG.error("Couldn't instantiate hidden class" + e);
57+
}
58+
}
59+
60+
}

0 commit comments

Comments
 (0)