Skip to content

Commit

Permalink
#943 Implement compilation of controllers using a built-in compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
ipolevoy committed Oct 11, 2019
1 parent f5b79fc commit 1bf7323
Showing 1 changed file with 34 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.javalite.activeweb;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import io.github.classgraph.ClassGraph;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static org.javalite.common.Collections.list;
import static org.javalite.common.Util.join;
Expand All @@ -18,7 +21,7 @@
*/
abstract public class DynamicClassFactory {

public static <T> T createInstance(String className, Class<T> expectedType) throws ClassLoadException {
static <T> T createInstance(String className, Class<T> expectedType) throws ClassLoadException {
try {
Object o = getCompiledClass(className).getDeclaredConstructor().newInstance();
T instance = expectedType.cast(o);
Expand All @@ -32,8 +35,9 @@ public static <T> T createInstance(String className, Class<T> expectedType) thro
}
}

private static Map<String, Class> cachedClasses = new HashMap<>();

public static Class getCompiledClass(String className) throws ClassLoadException{
static Class getCompiledClass(String className) throws ClassLoadException{
Class theClass;
try {
if (Configuration.activeReload()) {
Expand All @@ -44,15 +48,20 @@ public static Class getCompiledClass(String className) throws ClassLoadException
if (compilationResult.contains("error")) {
throw new CompilationException(compilationResult);
}

DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(ControllerFactory.class.getClassLoader(),
Configuration.getTargetDir());
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(ControllerFactory.class.getClassLoader(), Configuration.getTargetDir());
theClass = dynamicClassLoader.loadClass(className);
} else {
//TODO: in case there is no active_reload, cache instance of controller class - optimization!
theClass = Class.forName(className);
if(cachedClasses.containsKey(className)){
theClass = cachedClasses.get(className);
}else{
theClass = Class.forName(className);
try{
cachedClasses.put(className, theClass);
}catch(ConcurrentModificationException ignore){
//class already there, skipping
}
}
}

return theClass;
} catch (CompilationException e) {
throw e;
Expand All @@ -61,45 +70,33 @@ public static Class getCompiledClass(String className) throws ClassLoadException
}
}

protected synchronized static String compileClass(String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
private synchronized static String compileClass(String className) {

String controllerFileName = className.replace(".", System.getProperty("file.separator")) + ".java";
String classpath = getClasspath(new ClassGraph().getClasspathURLs());

URLClassLoader loader = ((URLClassLoader) Thread.currentThread().getContextClassLoader());
URL[] urls = loader.getURLs();

String classpath = getClasspath(urls);

StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
String targetClasses = join(list("target", "classes"), System.getProperty("file.separator"));
String srcMainJava = join(list("src", "main", "java"), System.getProperty("file.separator"));

String[] args = {"-g:lines,source,vars", "-d", targetClasses, "-cp", classpath, srcMainJava + System.getProperty("file.separator") + controllerFileName};

Class cl = Class.forName("com.sun.tools.javac.Main");
Method compile = cl.getMethod("compile", String[].class, PrintWriter.class);
compile.invoke(null, args, out);
out.flush();
return writer.toString();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String sourceFile = srcMainJava + System.getProperty("file.separator") + controllerFileName;
OutputStream errorStream = new ByteArrayOutputStream();
compiler.run(null, errorStream, errorStream, "-g:lines,source,vars" , "-g:lines,source,vars", "-d", targetClasses, "-cp", classpath, sourceFile);
return "Compiling: " + sourceFile + ", output: " + errorStream.toString();
}

private static String getClasspath(URL[] urls) {
String classpath = "";
private static String getClasspath(List<URL> urls) {
StringBuilder classpath = new StringBuilder();
for (URL url : urls) {
String path = url.getPath();
if(System.getProperty("os.name").contains("Windows")){
if(path.startsWith("/")){
path = path.substring(1);//loose leading slash
}
try{
path = URLDecoder.decode(path, "UTF-8");// fill in the spaces
}catch(java.io.UnsupportedEncodingException e){/*ignore*/}
path = URLDecoder.decode(path, StandardCharsets.UTF_8);// fill in the spaces
path = path.replace("/", "\\");//boy, do I dislike windoz!
}
classpath += path + System.getProperty("path.separator");
classpath.append(path).append(System.getProperty("path.separator"));
}

return classpath;
return classpath.toString();
}
}

0 comments on commit 1bf7323

Please sign in to comment.