/** * jetbrick-template * http://subchen.github.io/jetbrick-template/ * * Copyright 2010-2014 Guoqiang Chen. All rights reserved. * Email: subchen@gmail.com * * 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 jetbrick.template.compiler; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.*; import javax.tools.*; import javax.tools.JavaCompiler.CompilationTask; import jetbrick.template.utils.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 利用 JDK6 提供的 Java Compiler 进行编译,提供详细的错误输出。 */ public class JdkCompiler extends JavaCompiler { final Logger log = LoggerFactory.getLogger(JdkCompiler.class); private final boolean isJdk6; private javax.tools.JavaCompiler jc; private StandardJavaFileManager fileManager; private List<String> options; // 编译参数 public JdkCompiler() { String version = System.getProperty("java.version"); if (version != null && version.contains("1.6.")) { this.isJdk6 = true; } else { this.isJdk6 = false; } } @Override protected void initialize() { javax.tools.JavaCompiler jcc = ToolProvider.getSystemJavaCompiler(); if (jcc == null) { // JDT 支持 ServiceLoader 方式载入。 ServiceLoader<javax.tools.JavaCompiler> serviceLoader = ServiceLoader.load(javax.tools.JavaCompiler.class); Iterator<javax.tools.JavaCompiler> iterator = serviceLoader.iterator(); if (iterator.hasNext()) { jcc = iterator.next(); } } if (jcc == null) { throw new IllegalStateException("Can't get system java compiler. Please add jdk tools.jar to your classpath."); } this.jc = jcc; this.fileManager = jc.getStandardFileManager(null, null, null); this.options = Arrays.asList("-encoding", JavaSource.JAVA_FILE_ENCODING, "-g", "-nowarn", "-source", "1.6", "-target", "1.6"); setDefaultClasspath(fileManager); } private void setDefaultClasspath(StandardJavaFileManager fileManager) { ClassLoader contextClassLoader = ClassLoaderUtils.getContextClassLoader(); Collection<URL> classpath = ClassLoaderUtils.getClasspathURLs(contextClassLoader); // add dependences // @formatter:off List<String> classlist = Arrays.asList( "jetbrick.template.JetEngine", "javax.servlet.ServletContext", "org.slf4j.LoggerFactory" ); // @formatter:on for (String klass : classlist) { try { Class<?> cls = contextClassLoader.loadClass(klass); classpath.add(cls.getProtectionDomain().getCodeSource().getLocation()); } catch (ClassNotFoundException e) { } } if (classpath.size() > 0) { try { Set<File> files = new LinkedHashSet<File>(classpath.size() + 16); for (URL url : classpath) { File file = URLUtils.toFileObject(url); if (file.exists()) { files.add(file); } } Iterable<? extends File> list = fileManager.getLocation(StandardLocation.CLASS_PATH); for (File file : list) { files.add(file); } fileManager.setLocation(StandardLocation.CLASS_PATH, files); } catch (IOException e) { throw new IllegalStateException(e); } } // 输出编译用的 classpath if (debugEnabled) { if (log.isInfoEnabled()) { Iterable<? extends File> files = fileManager.getLocation(StandardLocation.CLASS_PATH); for (File file : files) { log.info("Compilation classpath: " + file.getAbsolutePath()); } } } } @Override protected void generateJavaClass(JavaSource source) { // 编译代码 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); // 编译器编译中的诊断信息 Iterable<? extends JavaFileObject> files = fileManager.getJavaFileObjects(source.getJavaFile()); // 要编译的所有Java文件 CompilationTask task = jc.getTask(null, fileManager, diagnostics, options, null, files); Boolean result; if (isJdk6) { // jdk6 的 compiler 是线程不安全的,需要手动同步 synchronized (this) { result = task.call(); } } else { // jdk7+ 的 compiler 是线程安全的 result = task.call(); } // 返回编译结果 if ((result == null) || !result.booleanValue()) { String[] sourceCodeLines = source.getSourceCode().split("\r?\n", -1); StringBuilder sb = new StringBuilder(); sb.append("Compilation failed."); sb.append('\n'); for (Diagnostic<? extends JavaFileObject> d : diagnostics.getDiagnostics()) { sb.append(d.getMessage(Locale.ENGLISH)).append('\n'); sb.append(StringUtils.getPrettyError(sourceCodeLines, (int) d.getLineNumber(), (int) d.getColumnNumber(), (int) d.getPosition(), (int) d.getPosition(), 3)); } sb.append(diagnostics.getDiagnostics().size()); sb.append(" error(s)\n"); throw new CompileErrorException(sb.toString()); } } }