/* * * Copyright 2013 Netflix, Inc. * * 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 com.netflix.nicobar.groovy2.internal.compile; import groovy.lang.GroovyClassLoader; import java.io.IOException; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.Phases; import org.codehaus.groovy.tools.GroovyClass; import com.netflix.nicobar.core.archive.ScriptArchive; import com.netflix.nicobar.core.compile.ScriptCompilationException; /** * Helper class for compiling Groovy files into classes. This class takes as it's input a collection * of {@link ScriptArchive}s and outputs a {@link GroovyClassLoader} with the classes pre-loaded into it. * * If a parent {@link ClassLoader} is not provided, the current thread context classloader is used. * * @author James Kojo * @author Vasanth Asokan */ public class Groovy2CompilerHelper { private final Path targetDir; private final List<Path> sourceFiles = new LinkedList<Path>(); private final List<ScriptArchive> scriptArchives = new LinkedList<ScriptArchive>(); private ClassLoader parentClassLoader; private CompilerConfiguration compileConfig; public Groovy2CompilerHelper(Path targetDir) { Objects.requireNonNull(targetDir, "targetDir"); this.targetDir = targetDir; } public Groovy2CompilerHelper withParentClassloader(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; return this; } public Groovy2CompilerHelper addSourceFile(Path groovyFile) { if (groovyFile != null) { sourceFiles.add(groovyFile); } return this; } public Groovy2CompilerHelper addScriptArchive(ScriptArchive archive) { if (archive != null) { scriptArchives.add(archive); } return this; } public Groovy2CompilerHelper withConfiguration(CompilerConfiguration compilerConfig) { if (compilerConfig != null) { this.compileConfig = compilerConfig; } return this; } /** * Compile the given source and load the resultant classes into a new {@link ClassNotFoundException} * @return initialized and laoded classes * @throws ScriptCompilationException */ @SuppressWarnings("unchecked") public Set<GroovyClass> compile() throws ScriptCompilationException { final CompilerConfiguration conf = compileConfig != null ? compileConfig: CompilerConfiguration.DEFAULT; conf.setTolerance(0); conf.setVerbose(true); conf.setTargetDirectory(targetDir.toFile()); final ClassLoader buildParentClassloader = parentClassLoader != null ? parentClassLoader : Thread.currentThread().getContextClassLoader(); GroovyClassLoader groovyClassLoader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() { public GroovyClassLoader run() { return new GroovyClassLoader(buildParentClassloader, conf, false); } }); CompilationUnit unit = new CompilationUnit(conf, null, groovyClassLoader); Set<String> scriptExtensions = conf.getScriptExtensions(); try { for (ScriptArchive scriptArchive : scriptArchives) { Set<String> entryNames = scriptArchive.getArchiveEntryNames(); for (String entryName : entryNames) { for (String extension : scriptExtensions) { if (entryName.endsWith(extension)) { // identified groovy file unit.addSource(scriptArchive.getEntry(entryName)); } } } } } catch (IOException e) { throw new ScriptCompilationException("Exception loading source files", e); } for (Path sourceFile : sourceFiles) { unit.addSource(sourceFile.toFile()); } try { unit.compile(Phases.OUTPUT); } catch (CompilationFailedException e) { throw new ScriptCompilationException("Exception during script compilation", e); } return new HashSet<GroovyClass>(unit.getClasses()); } }