| /* |
| * 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.util.Arrays; |
| import java.util.Map; |
| |
| import org.apache.drill.common.config.DrillConfig; |
| import org.apache.drill.common.exceptions.UserException; |
| import org.apache.drill.exec.compile.ClassTransformer.ClassNames; |
| import org.apache.drill.exec.exception.ClassTransformationException; |
| import org.apache.drill.exec.server.options.OptionMetaData; |
| import org.apache.drill.exec.server.options.OptionSet; |
| import org.apache.drill.exec.server.options.OptionValidator; |
| import org.apache.drill.exec.server.options.OptionValidator.OptionDescription; |
| import org.apache.drill.exec.server.options.OptionValue; |
| import org.apache.drill.exec.server.options.TypeValidators.BooleanValidator; |
| import org.apache.drill.exec.server.options.TypeValidators.LongValidator; |
| import org.apache.drill.exec.server.options.TypeValidators.StringValidator; |
| import org.codehaus.commons.compiler.CompileException; |
| |
| /** |
| * Selects between the two supported Java compilers: Janino and |
| * the build-in Java compiler. |
| * |
| * <h4>Session Options</h4> |
| * <dl> |
| * <dt>exec.java_compiler</dt> |
| * <dd>The compiler to use. Valid options are defined in the |
| * {@link ClassCompilerSelector.CompilerPolicy} enum.</dd> |
| * <dt>exec.java_compiler_debug</dt> |
| * <dd>If debug logging is enabled, then {@link AbstractClassCompiler} writes the |
| * generated Java code to the log file prior to compilation. This option |
| * adds line numbers to the logged code.</dd> |
| * <dt>exec.java_compiler_janino_maxsize</dt> |
| * <dd>The maximum size of code that the Janio compiler can handle. Larger code is |
| * handled by the JDK compiler. Defaults to 256K.</dd> |
| * </dl> |
| * <h4>Configuration Options</h4> |
| * Configuration options are used when the above session options are unset. |
| * <dl> |
| * <dt>drill.exec.compile.compiler</dt> |
| * <dd>Default for <var>exec.java_compiler</var></dd> |
| * <dt>drill.exec.compile.debug</dt> |
| * <dd>Default for <var>exec.java_compiler_debug</var></dd> |
| * <dt>drill.exec.compile.janino_maxsize</dt> |
| * <dd>Default for <var>exec.java_compiler_janino_maxsize</var></dd> |
| * </dl> |
| */ |
| |
| public class ClassCompilerSelector { |
| private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ClassCompilerSelector.class); |
| |
| public enum CompilerPolicy { |
| DEFAULT, JDK, JANINO; |
| } |
| |
| public static final String JAVA_COMPILER_JANINO_MAXSIZE_CONFIG = CodeCompiler.COMPILE_BASE + ".janino_maxsize"; |
| public static final String JAVA_COMPILER_DEBUG_CONFIG = CodeCompiler.COMPILE_BASE + ".debug"; |
| public static final String JAVA_COMPILER_CONFIG = CodeCompiler.COMPILE_BASE + ".compiler"; |
| |
| public static final String JAVA_COMPILER_OPTION = "exec.java_compiler"; |
| public static final String JAVA_COMPILER_JANINO_MAXSIZE_OPTION = "exec.java_compiler_janino_maxsize"; |
| public static final OptionValidator JAVA_COMPILER_JANINO_MAXSIZE = new LongValidator(JAVA_COMPILER_JANINO_MAXSIZE_OPTION, |
| new OptionDescription("See the exec.java_compiler option comment. Accepts inputs of type LONG.")); |
| |
| public static final String JAVA_COMPILER_DEBUG_OPTION = "exec.java_compiler_debug"; |
| public static final OptionValidator JAVA_COMPILER_DEBUG = new BooleanValidator(JAVA_COMPILER_DEBUG_OPTION, |
| new OptionDescription("Toggles the output of debug-level compiler error messages in runtime generated code.")); |
| |
| public static final StringValidator JAVA_COMPILER_VALIDATOR = new StringValidator(JAVA_COMPILER_OPTION, |
| new OptionDescription("Switches between DEFAULT, JDK, and JANINO mode for the current session. Uses Janino by default for generated source code of less than exec.java_compiler_janino_maxsize; otherwise, switches to the JDK compiler.")) { |
| @Override |
| public void validate(final OptionValue v, final OptionMetaData metaData, final OptionSet manager) { |
| super.validate(v, metaData, manager); |
| try { |
| CompilerPolicy.valueOf(v.string_val.toUpperCase()); |
| } catch (IllegalArgumentException e) { |
| throw UserException.validationError() |
| .message("Invalid value '%s' specified for option '%s'. Valid values are %s.", |
| v.string_val, getOptionName(), Arrays.toString(CompilerPolicy.values())) |
| .build(QueryClassLoader.logger); |
| } |
| } |
| }; |
| |
| private final CompilerPolicy policy; |
| private final long janinoThreshold; |
| |
| private final AbstractClassCompiler jdkClassCompiler; |
| private final AbstractClassCompiler janinoClassCompiler; |
| |
| public ClassCompilerSelector(ClassLoader classLoader, DrillConfig config, OptionSet sessionOptions) { |
| OptionValue value = sessionOptions.getOption(JAVA_COMPILER_OPTION); |
| policy = CompilerPolicy.valueOf((value != null) ? value.string_val.toUpperCase() : config.getString(JAVA_COMPILER_CONFIG).toUpperCase()); |
| |
| value = sessionOptions.getOption(JAVA_COMPILER_JANINO_MAXSIZE_OPTION); |
| janinoThreshold = (value != null) ? value.num_val : config.getLong(JAVA_COMPILER_JANINO_MAXSIZE_CONFIG); |
| |
| value = sessionOptions.getOption(JAVA_COMPILER_DEBUG_OPTION); |
| boolean debug = (value != null) ? value.bool_val : config.getBoolean(JAVA_COMPILER_DEBUG_CONFIG); |
| |
| janinoClassCompiler = (policy == CompilerPolicy.JANINO || policy == CompilerPolicy.DEFAULT) ? new JaninoClassCompiler(classLoader, debug) : null; |
| jdkClassCompiler = (policy == CompilerPolicy.JDK || policy == CompilerPolicy.DEFAULT) ? JDKClassCompiler.newInstance(classLoader, debug) : null; |
| |
| logger.info(String.format("Java compiler policy: %s, Debug option: %b", policy, debug)); |
| } |
| |
| byte[][] getClassByteCode(ClassNames className, String sourceCode) |
| throws CompileException, ClassNotFoundException, ClassTransformationException, IOException { |
| |
| byte[][] bc = getCompiler(sourceCode).getClassByteCode(className, sourceCode); |
| |
| // Uncomment the following to save the generated byte codes. |
| // Use the JDK javap command to view the generated code. |
| // This is the code from the compiler before byte code manipulations. |
| // For a similar block to display byte codes after manipulation, |
| // see QueryClassLoader. |
| |
| // final File baseDir = new File( new File( System.getProperty("java.io.tmpdir") ), "classes" ); |
| // for ( int i = 0; i < bc.length; i++ ) { |
| // File classFile = new File( baseDir, className.slash + i + ".class" ); |
| // classFile.getParentFile().mkdirs(); |
| // try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(classFile))) { |
| // out.write(bc[i]); |
| // } |
| // } |
| // System.out.println( "Classes saved to: " + baseDir.getAbsolutePath() ); |
| |
| return bc; |
| } |
| |
| public Map<String,byte[]> compile(ClassNames className, String sourceCode) |
| throws CompileException, ClassNotFoundException, ClassTransformationException, IOException { |
| return getCompiler(sourceCode).compile(className, sourceCode); |
| } |
| |
| private AbstractClassCompiler getCompiler(String sourceCode) { |
| if (jdkClassCompiler != null && |
| (policy == CompilerPolicy.JDK || (policy == CompilerPolicy.DEFAULT && sourceCode.length() > janinoThreshold))) { |
| return jdkClassCompiler; |
| } else { |
| return janinoClassCompiler; |
| } |
| } |
| } |