From 7061c63523c4aa13e3c761ace674f5f5dc1260fa Mon Sep 17 00:00:00 2001 From: Janne Hietamaki Date: Sat, 28 Apr 2012 14:57:26 +0300 Subject: [PATCH 1/4] Initial Java source file compilation: "vertx run Server.java" --- .../java/deploy/impl/VerticleManager.java | 6 + .../vertx/java/deploy/impl/VerticleType.java | 2 +- .../impl/java/CompilingClassLoader.java | 137 ++++++++++++++++++ .../impl/java/JavaSourceVerticleFactory.java | 32 ++++ .../deploy/impl/java/JavaVerticleFactory.java | 6 +- 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java create mode 100644 src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java diff --git a/src/main/java/org/vertx/java/deploy/impl/VerticleManager.java b/src/main/java/org/vertx/java/deploy/impl/VerticleManager.java index 14a579e89fc..ba7d4767844 100644 --- a/src/main/java/org/vertx/java/deploy/impl/VerticleManager.java +++ b/src/main/java/org/vertx/java/deploy/impl/VerticleManager.java @@ -28,6 +28,7 @@ import org.vertx.java.deploy.Container; import org.vertx.java.deploy.Verticle; import org.vertx.java.deploy.impl.groovy.GroovyVerticleFactory; +import org.vertx.java.deploy.impl.java.JavaSourceVerticleFactory; import org.vertx.java.deploy.impl.java.JavaVerticleFactory; import org.vertx.java.deploy.impl.jruby.JRubyVerticleFactory; import org.vertx.java.deploy.impl.rhino.RhinoVerticleFactory; @@ -208,6 +209,8 @@ private String doDeploy(boolean worker, String name, final String main, type = VerticleType.RUBY; } else if (main.endsWith(".groovy")) { type = VerticleType.GROOVY; + } else if (main.endsWith(".java")) { + type = VerticleType.JAVA_SOURCE; } final String deploymentName = name == null ? "deployment-" + UUID.randomUUID().toString() : name; @@ -229,6 +232,9 @@ private String doDeploy(boolean worker, String name, final String main, case GROOVY: verticleFactory = new GroovyVerticleFactory(vertx, this); break; + case JAVA_SOURCE: + verticleFactory = new JavaSourceVerticleFactory(this); + break; default: throw new IllegalArgumentException("Unsupported type: " + type); } diff --git a/src/main/java/org/vertx/java/deploy/impl/VerticleType.java b/src/main/java/org/vertx/java/deploy/impl/VerticleType.java index 6b8bc9eb827..fc82a5feeed 100644 --- a/src/main/java/org/vertx/java/deploy/impl/VerticleType.java +++ b/src/main/java/org/vertx/java/deploy/impl/VerticleType.java @@ -22,5 +22,5 @@ * @author Tim Fox */ public enum VerticleType implements Serializable { - JAVA, RUBY, GROOVY, JS + JAVA, RUBY, GROOVY, JS, JAVA_SOURCE } diff --git a/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java new file mode 100644 index 00000000000..ff24762a6ea --- /dev/null +++ b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java @@ -0,0 +1,137 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * 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.vertx.java.deploy.impl.java; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public class CompilingClassLoader extends ClassLoader { + private final String sourceName; + private final Map compiledClasses = new HashMap(); + + public CompilingClassLoader(ClassLoader loader, String sourceName) { + super(loader); + this.sourceName = sourceName; + compile(); + } + + private void compile() { + DiagnosticCollector diagnostics = new DiagnosticCollector(); + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + MemoryFileManager fileManager = new MemoryFileManager(javaCompiler.getStandardFileManager(null, null, null)); + JavaFileObject javaFile = new MemoryJavaFile(sourceName); + + JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, diagnostics, null, null, Collections.singleton(javaFile)); + boolean valid = task.call(); + for (Diagnostic d : diagnostics.getDiagnostics()) { + System.out.println(d); + } + if(!valid) { + throw new RuntimeException("Compilation failed!"); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + ByteArrayOutputStream bytecode = compiledClasses.get(name); + if (bytecode == null) { + throw new ClassNotFoundException(name); + } + return defineClass(name, bytecode.toByteArray(), 0, bytecode.size()); + } + + private class MemoryFileManager extends ForwardingJavaFileManager { + + private URI DUMMY_URI; + + { + try { + DUMMY_URI = new URI(""); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public MemoryFileManager(JavaFileManager fileManager) { + super(fileManager); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, final String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + return new SimpleJavaFileObject(DUMMY_URI, kind) { + public OutputStream openOutputStream() throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + compiledClasses.put(className, outputStream); + return outputStream; + } + }; + } + } + + private static class MemoryJavaFile extends SimpleJavaFileObject { + public MemoryJavaFile(String sourceName) { + super(sourceUri(sourceName), Kind.SOURCE); + } + + private static URI sourceUri(String sourceName) { + try { + return new URI(sourceName); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + StringBuffer content = new StringBuffer(); + Reader reader = null; + try { + reader = new BufferedReader(new FileReader(new File(getName()))); + char[] buf = new char[2000]; + int numRead = 0; + while ((numRead = reader.read(buf)) != -1) { + content.append(String.valueOf(buf, 0, numRead)); + } + return content; + } finally { + if (reader != null) { + reader.close(); + } + } + } + } +} diff --git a/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java b/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java new file mode 100644 index 00000000000..660852b632b --- /dev/null +++ b/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * 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.vertx.java.deploy.impl.java; + +import org.vertx.java.deploy.impl.VerticleManager; + +public class JavaSourceVerticleFactory extends JavaVerticleFactory { + + public JavaSourceVerticleFactory(VerticleManager mgr) { + super(mgr); + } + + @Override + protected Class loadClass(String main, ClassLoader cl) throws ClassNotFoundException { + String className = main.substring(0, main.length() - ".java".length()).replaceAll("/", "."); + return new CompilingClassLoader(cl, main).loadClass(className); + } +} diff --git a/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java b/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java index f40fcafbb55..281dc31a5d5 100644 --- a/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java +++ b/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java @@ -33,7 +33,7 @@ public JavaVerticleFactory(VerticleManager mgr) { public Verticle createVerticle(String main, ClassLoader cl) throws Exception { - Class clazz = cl.loadClass(main); + Class clazz = loadClass(main, cl); Verticle verticle = (Verticle)clazz.newInstance(); @@ -50,6 +50,10 @@ public Verticle createVerticle(String main, ClassLoader cl) throws Exception { } + protected Class loadClass(String main, ClassLoader cl) throws ClassNotFoundException { + return cl.loadClass(main); +} + public void reportException(Throwable t) { mgr.getLogger().error("Exception in Java verticle script", t); } From c6b8e9fabcb7164db3d18e8da478eac9d84c239f Mon Sep 17 00:00:00 2001 From: Janne Hietamaki Date: Sat, 12 May 2012 07:30:05 +0530 Subject: [PATCH 2/4] Plug compiling class loader into new JavaVerticleFactory --- .../impl/java/CompilingClassLoader.java | 7 ++++ .../impl/java/JavaSourceVerticleFactory.java | 32 ------------------- .../deploy/impl/java/JavaVerticleFactory.java | 25 +++++++++------ 3 files changed, 22 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java diff --git a/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java index ff24762a6ea..4b78a84cbc8 100644 --- a/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java +++ b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java @@ -39,6 +39,9 @@ import javax.tools.SimpleJavaFileObject; import javax.tools.ToolProvider; +/** + * @author Janne Hietamäki + */ public class CompilingClassLoader extends ClassLoader { private final String sourceName; private final Map compiledClasses = new HashMap(); @@ -49,6 +52,10 @@ public CompilingClassLoader(ClassLoader loader, String sourceName) { compile(); } + public String resolveMainClassName() { + return sourceName.substring(0, sourceName.length() - ".java".length()); + } + private void compile() { DiagnosticCollector diagnostics = new DiagnosticCollector(); JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); diff --git a/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java b/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java deleted file mode 100644 index 660852b632b..00000000000 --- a/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceVerticleFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2011-2012 the original author or authors. - * - * 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.vertx.java.deploy.impl.java; - -import org.vertx.java.deploy.impl.VerticleManager; - -public class JavaSourceVerticleFactory extends JavaVerticleFactory { - - public JavaSourceVerticleFactory(VerticleManager mgr) { - super(mgr); - } - - @Override - protected Class loadClass(String main, ClassLoader cl) throws ClassNotFoundException { - String className = main.substring(0, main.length() - ".java".length()).replaceAll("/", "."); - return new CompilingClassLoader(cl, main).loadClass(className); - } -} diff --git a/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java b/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java index 8ff593a9945..baa270f63a0 100644 --- a/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java +++ b/src/main/java/org/vertx/java/deploy/impl/java/JavaVerticleFactory.java @@ -43,8 +43,7 @@ public String getLanguage() { @Override public boolean isFactoryFor(String main) { - if (main.endsWith(".java")) { - // TODO requires dynamic compiler + if (isJavaSource(main)) { return true; } if (main.endsWith(".class")) { @@ -55,10 +54,20 @@ public boolean isFactoryFor(String main) { } return false; } + + private boolean isJavaSource(String main) { + return main.endsWith(".java"); + } - public Verticle createVerticle(String main, ClassLoader cl) throws Exception { - - Class clazz = cl.loadClass(main); + public Verticle createVerticle(String main, ClassLoader loader) throws Exception { + ClassLoader cl = loader; + String className = main; + if(isJavaSource(main)) { + CompilingClassLoader compilingLoader = new CompilingClassLoader(loader, main); + className = compilingLoader.resolveMainClassName(); + cl = compilingLoader; + } + Class clazz = cl.loadClass(className); Verticle verticle = (Verticle) clazz.newInstance(); @@ -74,11 +83,7 @@ public Verticle createVerticle(String main, ClassLoader cl) throws Exception { return verticle; } - - protected Class loadClass(String main, ClassLoader cl) throws ClassNotFoundException { - return cl.loadClass(main); -} - + public void reportException(Throwable t) { mgr.getLogger().error("Exception in Java verticle script", t); } From 5fadef805ea0ab4dcfff60aff5615af9cf496ea7 Mon Sep 17 00:00:00 2001 From: Janne Hietamaki Date: Sat, 12 May 2012 09:25:18 +0530 Subject: [PATCH 3/4] Dynamic loading of .java files --- .../impl/java/CompilingClassLoader.java | 135 +++++------------- .../deploy/impl/java/MemoryFileManager.java | 68 +++++++++ 2 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 src/main/java/org/vertx/java/deploy/impl/java/MemoryFileManager.java diff --git a/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java index 4b78a84cbc8..e4b35ec6120 100644 --- a/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java +++ b/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java @@ -16,129 +16,68 @@ package org.vertx.java.deploy.impl.java; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.OutputStream; -import java.io.Reader; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaCompiler; -import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; import javax.tools.ToolProvider; /** + * + * Classloader for dynamic .java source file compilation and loading. + * * @author Janne Hietamäki */ public class CompilingClassLoader extends ClassLoader { - private final String sourceName; - private final Map compiledClasses = new HashMap(); + private final File sourceFile; + private final MemoryFileManager fileManager; public CompilingClassLoader(ClassLoader loader, String sourceName) { super(loader); - this.sourceName = sourceName; - compile(); + this.sourceFile = new File(sourceName).getAbsoluteFile(); + if(!this.sourceFile.canRead()) { + throw new RuntimeException("File not found: " + sourceName); + } + try { + DiagnosticCollector diagnostics = new DiagnosticCollector(); + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null); + + standardFileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.singleton(sourceFile.getParentFile())); + + fileManager = new MemoryFileManager(standardFileManager); + JavaFileObject javaFile = standardFileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, resolveMainClassName(), Kind.SOURCE); + JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, diagnostics, null, null, Collections.singleton(javaFile)); + boolean valid = task.call(); + + for (Diagnostic d : diagnostics.getDiagnostics()) { + System.out.println(d); + } + if (!valid) { + throw new RuntimeException("Compilation failed!"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } } public String resolveMainClassName() { - return sourceName.substring(0, sourceName.length() - ".java".length()); - } - - private void compile() { - DiagnosticCollector diagnostics = new DiagnosticCollector(); - JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); - MemoryFileManager fileManager = new MemoryFileManager(javaCompiler.getStandardFileManager(null, null, null)); - JavaFileObject javaFile = new MemoryJavaFile(sourceName); - - JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, diagnostics, null, null, Collections.singleton(javaFile)); - boolean valid = task.call(); - for (Diagnostic d : diagnostics.getDiagnostics()) { - System.out.println(d); - } - if(!valid) { - throw new RuntimeException("Compilation failed!"); - } + String fileName = sourceFile.getName(); + return fileName.substring(0, fileName.length() - Kind.SOURCE.extension.length()); } @Override protected Class findClass(String name) throws ClassNotFoundException { - ByteArrayOutputStream bytecode = compiledClasses.get(name); + byte[] bytecode = fileManager.getCompiledClass(name); if (bytecode == null) { throw new ClassNotFoundException(name); } - return defineClass(name, bytecode.toByteArray(), 0, bytecode.size()); - } - - private class MemoryFileManager extends ForwardingJavaFileManager { - - private URI DUMMY_URI; - - { - try { - DUMMY_URI = new URI(""); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - public MemoryFileManager(JavaFileManager fileManager) { - super(fileManager); - } - - @Override - public JavaFileObject getJavaFileForOutput(Location location, final String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { - return new SimpleJavaFileObject(DUMMY_URI, kind) { - public OutputStream openOutputStream() throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - compiledClasses.put(className, outputStream); - return outputStream; - } - }; - } - } - - private static class MemoryJavaFile extends SimpleJavaFileObject { - public MemoryJavaFile(String sourceName) { - super(sourceUri(sourceName), Kind.SOURCE); - } - - private static URI sourceUri(String sourceName) { - try { - return new URI(sourceName); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - StringBuffer content = new StringBuffer(); - Reader reader = null; - try { - reader = new BufferedReader(new FileReader(new File(getName()))); - char[] buf = new char[2000]; - int numRead = 0; - while ((numRead = reader.read(buf)) != -1) { - content.append(String.valueOf(buf, 0, numRead)); - } - return content; - } finally { - if (reader != null) { - reader.close(); - } - } - } + return defineClass(name, bytecode, 0, bytecode.length); } } diff --git a/src/main/java/org/vertx/java/deploy/impl/java/MemoryFileManager.java b/src/main/java/org/vertx/java/deploy/impl/java/MemoryFileManager.java new file mode 100644 index 00000000000..a414653a3b4 --- /dev/null +++ b/src/main/java/org/vertx/java/deploy/impl/java/MemoryFileManager.java @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * 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.vertx.java.deploy.impl.java; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; + +/** + * Java in-memory file manager used by {@link CompilingClassLoader} to handle + * compiled classes + * + * @author Janne Hietamäki + */ +public class MemoryFileManager extends ForwardingJavaFileManager { + private final Map compiledClasses = new HashMap(); + + public MemoryFileManager(JavaFileManager fileManager) { + super(fileManager); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, final String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + try { + return new SimpleJavaFileObject(new URI(""), kind) { + public OutputStream openOutputStream() throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + compiledClasses.put(className, outputStream); + return outputStream; + } + }; + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public byte[] getCompiledClass(String name) { + ByteArrayOutputStream bytes = compiledClasses.get(name); + if (bytes == null) { + return null; + } + return bytes.toByteArray(); + } +} \ No newline at end of file From 379369ff9cb0b45fdc771397c32081d78d506c05 Mon Sep 17 00:00:00 2001 From: Janne Hietamaki Date: Sat, 12 May 2012 10:04:09 +0530 Subject: [PATCH 4/4] remove type --- src/main/java/org/vertx/java/deploy/impl/VerticleType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/vertx/java/deploy/impl/VerticleType.java b/src/main/java/org/vertx/java/deploy/impl/VerticleType.java index ce0a7038a13..ff05c904da8 100644 --- a/src/main/java/org/vertx/java/deploy/impl/VerticleType.java +++ b/src/main/java/org/vertx/java/deploy/impl/VerticleType.java @@ -23,5 +23,5 @@ */ @Deprecated public enum VerticleType implements Serializable { - JAVA, RUBY, GROOVY, JS, JAVA_SOURCE + JAVA, RUBY, GROOVY, JS }