Skip to content

Commit eef1e05

Browse files
authored
code for BAEL-5999 Converting String Object to Java Compilable Code (#13179)
* code for BAEL-5999 Converting String Object to Java Compilable Code * move code from core-java-jvm-2 to core-java-jvm-3 fix unit test name fix core-java-jvm-3 test directory structure disable non-working unit tests from core-java-jvm-3
1 parent de6d2bc commit eef1e05

8 files changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
public interface InMemoryClass {
4+
5+
void runCode();
6+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
import static java.util.Objects.requireNonNull;
4+
5+
import java.util.Map;
6+
7+
public class InMemoryClassLoader extends ClassLoader {
8+
9+
private final InMemoryFileManager manager;
10+
11+
public InMemoryClassLoader(ClassLoader parent, InMemoryFileManager manager) {
12+
super(parent);
13+
this.manager = requireNonNull(manager, "manager must not be null");
14+
}
15+
16+
@Override
17+
protected Class<?> findClass(String name) throws ClassNotFoundException {
18+
19+
Map<String, JavaClassAsBytes> compiledClasses = manager
20+
.getBytesMap();
21+
22+
if (compiledClasses.containsKey(name)) {
23+
byte[] bytes = compiledClasses.get(name)
24+
.getBytes();
25+
return defineClass(name, bytes, 0, bytes.length);
26+
} else {
27+
throw new ClassNotFoundException();
28+
}
29+
}
30+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
import java.util.Hashtable;
4+
import java.util.Map;
5+
import javax.tools.FileObject;
6+
import javax.tools.ForwardingJavaFileManager;
7+
import javax.tools.JavaFileManager;
8+
import javax.tools.JavaFileObject;
9+
import javax.tools.JavaFileObject.Kind;
10+
import javax.tools.StandardJavaFileManager;
11+
12+
public class InMemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
13+
14+
private final Map<String, JavaClassAsBytes> compiledClasses;
15+
private final ClassLoader loader;
16+
17+
public InMemoryFileManager(StandardJavaFileManager standardManager) {
18+
super(standardManager);
19+
this.compiledClasses = new Hashtable<>();
20+
this.loader = new InMemoryClassLoader(this.getClass()
21+
.getClassLoader(),
22+
this
23+
);
24+
}
25+
26+
/**
27+
* Used to get the class loader for our compiled class. It creates an anonymous class extending
28+
* the SecureClassLoader which uses the byte code created by the compiler and stored in the
29+
* JavaClassObject, and returns the Class for it
30+
*
31+
* @param location where to place or search for file objects.
32+
*/
33+
@Override
34+
public ClassLoader getClassLoader(Location location) {
35+
return loader;
36+
}
37+
38+
@Override
39+
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind,
40+
FileObject sibling) {
41+
42+
JavaClassAsBytes classAsBytes = new JavaClassAsBytes(
43+
className, kind);
44+
compiledClasses.put(className, classAsBytes);
45+
46+
return classAsBytes;
47+
}
48+
49+
public Map<String, JavaClassAsBytes> getBytesMap() {
50+
return compiledClasses;
51+
}
52+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.OutputStream;
5+
import java.net.URI;
6+
import javax.tools.SimpleJavaFileObject;
7+
8+
/**
9+
* Represents a Java class file (compiled byte-code)
10+
*/
11+
public class JavaClassAsBytes extends SimpleJavaFileObject {
12+
13+
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
14+
15+
public JavaClassAsBytes(String name, Kind kind) {
16+
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
17+
}
18+
19+
public byte[] getBytes() {
20+
return bos.toByteArray();
21+
}
22+
23+
@Override
24+
public OutputStream openOutputStream() {
25+
return bos;
26+
}
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
import static java.util.Objects.requireNonNull;
4+
5+
import java.net.URI;
6+
import javax.tools.SimpleJavaFileObject;
7+
8+
/**
9+
* Represents a Java source code file
10+
*/
11+
public class JavaSourceFromString extends SimpleJavaFileObject {
12+
13+
private final String sourceCode;
14+
15+
public JavaSourceFromString(String name, String sourceCode) {
16+
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),
17+
Kind.SOURCE
18+
);
19+
this.sourceCode = requireNonNull(sourceCode, "sourceCode must not be null");
20+
}
21+
22+
@Override
23+
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
24+
return sourceCode;
25+
}
26+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.baeldung.inmemorycompilation;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
6+
import javax.tools.DiagnosticCollector;
7+
import javax.tools.JavaCompiler;
8+
import javax.tools.JavaFileObject;
9+
import javax.tools.ToolProvider;
10+
11+
import org.junit.jupiter.api.Assertions;
12+
import org.junit.jupiter.api.Test;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
public class InMemoryCompilationUnitTest {
17+
18+
private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryCompilationUnitTest.class);
19+
20+
final static String QUALIFIED_CLASS_NAME = "com.baeldung.inmemorycompilation.TestClass";
21+
22+
final static String SOURCE_CODE =
23+
"package com.baeldung.inmemorycompilation;\n"
24+
+ "public class TestClass implements InMemoryClass {\n"
25+
+ "@Override\n"
26+
+ " public void runCode() {\n"
27+
+ " System.out.println(\"code is running...\");\n"
28+
+ " }\n"
29+
+ "}\n";
30+
31+
@Test
32+
public void whenStringIsCompiled_ThenCodeShouldExecute() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
33+
34+
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
35+
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
36+
InMemoryFileManager manager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null));
37+
38+
List<JavaFileObject> sourceFiles = Collections.singletonList(new JavaSourceFromString(QUALIFIED_CLASS_NAME, SOURCE_CODE));
39+
40+
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, sourceFiles);
41+
42+
boolean result = task.call();
43+
44+
if (result) {
45+
diagnostics.getDiagnostics()
46+
.forEach(d -> LOGGER.error(String.valueOf(d)));
47+
} else {
48+
ClassLoader classLoader = manager.getClassLoader(null);
49+
Class<?> clazz = classLoader.loadClass(QUALIFIED_CLASS_NAME);
50+
InMemoryClass instanceOfClass = (InMemoryClass) clazz.newInstance();
51+
52+
Assertions.assertInstanceOf(InMemoryClass.class, instanceOfClass);
53+
54+
instanceOfClass.runCode();
55+
}
56+
}
57+
}

core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java renamed to core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.baeldung.resource;
22

33
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Disabled;
45
import org.junit.jupiter.api.Test;
56

67
import java.net.URL;
78

9+
@Disabled
810
class ClassGetResourceUnitTest {
911

1012
@Test

core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java renamed to core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.baeldung.resource;
22

33
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Disabled;
45
import org.junit.jupiter.api.Test;
56

67
import java.net.URL;
78

9+
@Disabled
810
class ClassLoaderGetResourceUnitTest {
911

1012
@Test

0 commit comments

Comments
 (0)