diff --git a/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java b/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java index aba462dddc3..d59effcb90e 100644 --- a/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java +++ b/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/CompilingClassLoader.java @@ -20,6 +20,7 @@ import org.vertx.java.core.logging.impl.LoggerFactory; import javax.tools.*; +import java.net.URL; import javax.tools.JavaFileObject.Kind; import java.io.File; import java.util.Collections; @@ -34,31 +35,28 @@ public class CompilingClassLoader extends ClassLoader { private static final Logger log = LoggerFactory.getLogger(CompilingClassLoader.class); - private final String className; + private final JavaSourceContext javaSourceContext; private final MemoryFileManager fileManager; - private static final String FILE_SEP = System.getProperty("file.separator"); - public CompilingClassLoader(ClassLoader loader, String sourceName) { super(loader); - String temp = sourceName.replace(FILE_SEP, "."); - this.className = temp.substring(0, sourceName.length() - Kind.SOURCE.extension.length()); - File sourceFile = new File(sourceName).getAbsoluteFile(); + URL resource = getResource(sourceName); + if(resource == null) { + throw new RuntimeException("Resource not found: " + sourceName); + } + File sourceFile = new File(resource.getFile()); if (!sourceFile.canRead()) { throw new RuntimeException("File not found: " + sourceName); } + + this.javaSourceContext = new JavaSourceContext(sourceFile); + try { DiagnosticCollector diagnostics = new DiagnosticCollector<>(); JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null); - File sourceRoot; - if (sourceName.contains(FILE_SEP)) { - sourceRoot = new File(".").getAbsoluteFile(); - } else { - sourceRoot = sourceFile.getParentFile(); - } - standardFileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.singleton(sourceRoot)); + standardFileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.singleton(javaSourceContext.getSourceRoot())); fileManager = new MemoryFileManager(loader, standardFileManager); JavaFileObject javaFile = standardFileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, resolveMainClassName(), Kind.SOURCE); JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, diagnostics, null, null, Collections.singleton(javaFile)); @@ -79,7 +77,7 @@ public CompilingClassLoader(ClassLoader loader, String sourceName) { } public String resolveMainClassName() { - return className; + return javaSourceContext.getClassName(); } @Override diff --git a/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceContext.java b/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceContext.java new file mode 100644 index 00000000000..9730c0be704 --- /dev/null +++ b/vertx-lang/vertx-lang-java/src/main/java/org/vertx/java/deploy/impl/java/JavaSourceContext.java @@ -0,0 +1,98 @@ +/* + * Copyright 2013 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.core.logging.Logger; +import org.vertx.java.core.logging.impl.LoggerFactory; + +import javax.tools.*; +import java.util.List; +import java.net.URL; +import javax.tools.JavaFileObject.Kind; +import java.io.*; +import java.nio.file.Files; +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +/** + * + * Resolves package name and source path based on a single Java source file. + * + * @author Janne Hietamäki + */ +public class JavaSourceContext { + private static final String FILE_SEP = System.getProperty("file.separator"); + private final static String REMOVE_COMMENTS_REGEXP = "(?://.*)|(/\\*(?:.|[\\n\\r])*?\\*/)"; + + private final String className; + private final File sourceRoot; + + public JavaSourceContext(File file) { + String packageName = parsePackage(file); + File rootDirectory = file.getParentFile(); + if(packageName != null) { + for(String token : packageName.split("\\.")) { + if(!token.equals(rootDirectory.getName())) { + throw new RuntimeException("Package structure does not match directory structure: " + token + " != " + rootDirectory.getName()); + } + rootDirectory = rootDirectory.getParentFile(); + } + } + sourceRoot = rootDirectory; + + String fileName = file.getName(); + String className = fileName.substring(0, fileName.length() - Kind.SOURCE.extension.length()); + if(packageName != null) { + className = packageName + "." + className; + } + this.className = className; + } + + public File getSourceRoot() { + return sourceRoot; + } + + public String getClassName() { + return className; + } + + /* + * Parse package definition from a Java source file: + * First remove all comments, split file into lines, find first non-empty line + * Then, if the line starts with keyword "package", parse the package definition from it. + * + */ + private String parsePackage(File file) { + try { + String source = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + source = source.replaceAll(REMOVE_COMMENTS_REGEXP, " "); + for(String line : source.split("\\r?\\n")) { + line = line.trim(); + if(line.length() > 0) { + int idx = line.indexOf("package "); + if(idx != -1) { + return line.substring(line.indexOf(" ", idx), line.indexOf(";", idx)).trim(); + } + return null; // Package definition must be on the first non-comment line + } + } + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file