blob: c661f5defc4076637b7ed1724fa2e16cd4cc514a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.drill.exec.compile;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import org.apache.drill.exec.compile.ClassTransformer.ClassNames;
import org.codehaus.commons.compiler.CompileException;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class JDKClassCompiler extends AbstractClassCompiler {
private static final Logger logger = LoggerFactory.getLogger(JDKClassCompiler.class);
private final Collection<String> compilerOptions;
private final DiagnosticListener<JavaFileObject> listener;
private final JavaCompiler compiler;
private final ClassLoader classLoader;
private JDKClassCompiler(JavaCompiler compiler, ClassLoader classLoader, boolean debug) {
super(debug);
this.compiler = compiler;
this.listener = new DrillDiagnosticListener();
this.classLoader = classLoader;
this.compilerOptions = Lists.newArrayList(this.debug ? "-g:source,lines,vars" : "-g:none");
}
public static JDKClassCompiler newInstance(ClassLoader classLoader, boolean debug) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new RuntimeException("JDK Java compiler not available - probably you're running Drill with a JRE and not a JDK");
}
return new JDKClassCompiler(compiler, classLoader, debug);
}
@Override
protected byte[][] getByteCode(final ClassNames className, final String sourceCode)
throws CompileException, IOException, ClassNotFoundException {
return doCompile(className, sourceCode).getByteCode();
}
@Override
public Map<String, byte[]> compile(final ClassNames className, final String sourceCode)
throws CompileException, IOException, ClassNotFoundException {
return doCompile(className, sourceCode).getClassByteCodes();
}
private DrillJavaFileObject doCompile(final ClassNames className, final String sourceCode)
throws CompileException, IOException, ClassNotFoundException {
// JavaFileManager should be closed after its usage to release all resources opened by this file manager
try (JavaFileManager fileManager = new DrillJavaFileManager(
compiler.getStandardFileManager(listener, null, StandardCharsets.UTF_8), classLoader)) {
// Create one Java source file in memory, which will be compiled later.
DrillJavaFileObject compilationUnit = new DrillJavaFileObject(className.dot, sourceCode);
CompilationTask task = compiler.getTask(null, fileManager, listener, compilerOptions, null, Collections.singleton(compilationUnit));
// Run the compiler.
if (!task.call()) {
throw new CompileException("Compilation failed", null);
} else if (!compilationUnit.isCompiled()) {
throw new ClassNotFoundException(className + ": Class file not created by compilation.");
}
// all good
return compilationUnit;
} catch (RuntimeException rte) {
// Unwrap the compilation exception and throw it.
Throwable cause = rte.getCause();
if (cause != null) {
cause = cause.getCause();
if (cause instanceof CompileException) {
throw (CompileException) cause;
}
if (cause instanceof IOException) {
throw (IOException) cause;
}
}
throw rte;
}
}
@Override
protected Logger getLogger() {
return logger;
}
}