
org.fuwjax.oss.compile.RuntimeClassLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jerc Show documentation
Show all versions of jerc Show documentation
Runtime code generation support
/*
* Copyright (C) 2015 fuwjax.org ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fuwjax.oss.compile;
import org.fuwjax.oss.compile.file.ClassFileObject;
import org.fuwjax.oss.compile.file.StringSourceFileObject;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static javax.tools.ToolProvider.getSystemJavaCompiler;
import static org.fuwjax.oss.compile.file.BaseFileObject.kindOf;
import static org.fuwjax.oss.compile.file.BaseFileObject.toUri;
/**
* A ClassLoader which compiles source code at runtime in memory.
*/
public class RuntimeClassLoader extends ClassLoader {
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final JavaCompiler compiler = getSystemJavaCompiler();
private final ConcurrentMap files = new ConcurrentHashMap<>();
private final ForwardingJavaFileManager manager = new ForwardingJavaFileManager(
compiler.getStandardFileManager(null, null, null)) {
@Override
public FileObject getFileForOutput(final Location location, final String packageName, final String relativeName, final FileObject sibling) throws IOException {
final String className = className(packageName, relativeName);
return getJavaFileForOutput(location, className, kindOf(className), sibling);
}
private String className(final String packageName, final String relativeName) {
return "".equals(packageName) ? relativeName : packageName.replaceAll("\\.", "/") + "/" + relativeName;
}
@Override
public FileObject getFileForInput(final Location location, final String packageName, final String relativeName) throws IOException {
final String className = className(packageName, relativeName);
final Kind kind = kindOf(className);
FileObject file = files.get(toUri(className, kind));
if(file == null) {
file = super.getFileForInput(location, packageName, relativeName);
if(file == null) {
file = new ClassFileObject(className, kind, UTF_8);
}
}
return file;
}
@Override
public JavaFileObject getJavaFileForInput(final Location location, final String className, final Kind kind) throws IOException {
JavaFileObject file = files.get(toUri(className, kind));
if(file == null) {
file = super.getJavaFileForInput(location, className, kind);
if(file == null) {
file = new ClassFileObject(className, kind, UTF_8);
}
}
return file;
}
@Override
public boolean isSameFile(final FileObject a, final FileObject b) {
return a.toUri().equals(b.toUri());
}
@Override
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind,
final FileObject sibling) throws IOException {
final ClassFileObject file = new ClassFileObject(className, kind, UTF_8);
final ClassFileObject old = files.putIfAbsent(toUri(className, kind), file);
return old == null ? file : old;
}
};
private final Writer log;
public RuntimeClassLoader() {
log = new StringWriter();
}
public RuntimeClassLoader(final OutputStream logStream) {
log = new OutputStreamWriter(logStream);
}
public boolean compile(final Path root, final String... options) throws IOException {
final Set compUnit = new HashSet<>();
Files.walkFileTree(root, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if(file.toString().endsWith(".java")) {
final String name = root.relativize(file).toString();
compUnit.add(new StringSourceFileObject(name, new String(Files.readAllBytes(file))));
}
return FileVisitResult.CONTINUE;
}
});
return compile(compUnit, options);
}
/**
* Compiles the {@code source} java code into the class {@code name}.
* @param sources the java source code
* @param options the compiler options
* @return true if the source code compiled, false otherwise
*/
public boolean compile(final Map sources, final String... options) {
final Set compUnit = sources.entrySet().stream().map(entry -> new StringSourceFileObject(entry.getKey(), entry.getValue())).collect(Collectors.toSet());
return compile(compUnit, options);
}
public boolean compile(final String name, final String source, final String... options) {
return compile(Collections.singletonMap(name, source), options);
}
public boolean compile(final Set extends JavaFileObject> compUnit, final String... options) {
return compiler.getTask(log, manager, null, asList(options), null, compUnit).call();
}
@Override
protected URL findResource(final String name) {
final ClassFileObject file = files.get(toUri(name, Kind.OTHER));
if(file == null) {
return null;
}
try {
return new URL("memory", "localhost", -1, name, new URLStreamHandler() {
@Override
protected URLConnection openConnection(final URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
// do nothing
}
@Override
public InputStream getInputStream() throws IOException {
return file.openInputStream();
}
};
}
});
} catch(final MalformedURLException e) {
throw new RuntimeException("RuntimeClassLoader bug - report immediately", e);
}
}
@Override
protected Enumeration findResources(final String name) throws IOException {
return enumeration(singleton(findResource(name)));
}
@Override
protected Class> findClass(final String name) throws ClassNotFoundException {
final ClassFileObject file = files.get(toUri(name, Kind.CLASS));
if(file == null) {
throw new ClassNotFoundException(name);
}
final byte[] b = file.toBytes();
return defineClass(name, b, 0, b.length);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy