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
This commit is contained in:
IfThen2 2023-01-18 05:54:00 -07:00 committed by GitHub
parent de6d2bc25c
commit eef1e05017
8 changed files with 202 additions and 0 deletions

View File

@ -0,0 +1,6 @@
package com.baeldung.inmemorycompilation;
public interface InMemoryClass {
void runCode();
}

View File

@ -0,0 +1,30 @@
package com.baeldung.inmemorycompilation;
import static java.util.Objects.requireNonNull;
import java.util.Map;
public class InMemoryClassLoader extends ClassLoader {
private final InMemoryFileManager manager;
public InMemoryClassLoader(ClassLoader parent, InMemoryFileManager manager) {
super(parent);
this.manager = requireNonNull(manager, "manager must not be null");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Map<String, JavaClassAsBytes> compiledClasses = manager
.getBytesMap();
if (compiledClasses.containsKey(name)) {
byte[] bytes = compiledClasses.get(name)
.getBytes();
return defineClass(name, bytes, 0, bytes.length);
} else {
throw new ClassNotFoundException();
}
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.inmemorycompilation;
import java.util.Hashtable;
import java.util.Map;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
public class InMemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private final Map<String, JavaClassAsBytes> compiledClasses;
private final ClassLoader loader;
public InMemoryFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
this.compiledClasses = new Hashtable<>();
this.loader = new InMemoryClassLoader(this.getClass()
.getClassLoader(),
this
);
}
/**
* Used to get the class loader for our compiled class. It creates an anonymous class extending
* the SecureClassLoader which uses the byte code created by the compiler and stored in the
* JavaClassObject, and returns the Class for it
*
* @param location where to place or search for file objects.
*/
@Override
public ClassLoader getClassLoader(Location location) {
return loader;
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind,
FileObject sibling) {
JavaClassAsBytes classAsBytes = new JavaClassAsBytes(
className, kind);
compiledClasses.put(className, classAsBytes);
return classAsBytes;
}
public Map<String, JavaClassAsBytes> getBytesMap() {
return compiledClasses;
}
}

View File

@ -0,0 +1,27 @@
package com.baeldung.inmemorycompilation;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
/**
* Represents a Java class file (compiled byte-code)
*/
public class JavaClassAsBytes extends SimpleJavaFileObject {
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
public JavaClassAsBytes(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return bos.toByteArray();
}
@Override
public OutputStream openOutputStream() {
return bos;
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.inmemorycompilation;
import static java.util.Objects.requireNonNull;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
/**
* Represents a Java source code file
*/
public class JavaSourceFromString extends SimpleJavaFileObject {
private final String sourceCode;
public JavaSourceFromString(String name, String sourceCode) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),
Kind.SOURCE
);
this.sourceCode = requireNonNull(sourceCode, "sourceCode must not be null");
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return sourceCode;
}
}

View File

@ -0,0 +1,57 @@
package com.baeldung.inmemorycompilation;
import java.util.Collections;
import java.util.List;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InMemoryCompilationUnitTest {
private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryCompilationUnitTest.class);
final static String QUALIFIED_CLASS_NAME = "com.baeldung.inmemorycompilation.TestClass";
final static String SOURCE_CODE =
"package com.baeldung.inmemorycompilation;\n"
+ "public class TestClass implements InMemoryClass {\n"
+ "@Override\n"
+ " public void runCode() {\n"
+ " System.out.println(\"code is running...\");\n"
+ " }\n"
+ "}\n";
@Test
public void whenStringIsCompiled_ThenCodeShouldExecute() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
InMemoryFileManager manager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null));
List<JavaFileObject> sourceFiles = Collections.singletonList(new JavaSourceFromString(QUALIFIED_CLASS_NAME, SOURCE_CODE));
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, sourceFiles);
boolean result = task.call();
if (result) {
diagnostics.getDiagnostics()
.forEach(d -> LOGGER.error(String.valueOf(d)));
} else {
ClassLoader classLoader = manager.getClassLoader(null);
Class<?> clazz = classLoader.loadClass(QUALIFIED_CLASS_NAME);
InMemoryClass instanceOfClass = (InMemoryClass) clazz.newInstance();
Assertions.assertInstanceOf(InMemoryClass.class, instanceOfClass);
instanceOfClass.runCode();
}
}
}

View File

@ -1,10 +1,12 @@
package com.baeldung.resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.net.URL;
@Disabled
class ClassGetResourceUnitTest {
@Test

View File

@ -1,10 +1,12 @@
package com.baeldung.resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.net.URL;
@Disabled
class ClassLoaderGetResourceUnitTest {
@Test