com.disney.groovity.Groovity Maven / Gradle / Ivy
/*******************************************************************************
* © 2018 Disney | ABC Television Group
*
* Licensed under the Apache License, Version 2.0 (the "Apache License")
* with the following modification; you may not use this file except in
* compliance with the Apache License and the following modification to it:
* Section 6. Trademarks. is deleted and replaced with:
*
* 6. Trademarks. This License does not grant permission to use the trade
* names, trademarks, service marks, or product names of the Licensor
* and its affiliates, except as required to comply with Section 4(c) of
* the License and to reproduce the content of the NOTICE file.
*
* You may obtain a copy of the Apache License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Apache License with the above modification is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the Apache License for the specific
* language governing permissions and limitations under the Apache License.
*******************************************************************************/
package com.disney.groovity;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import groovy.transform.Trait;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.Janitor;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.codehaus.groovy.tools.GroovyClass;
import org.codehaus.groovy.tools.Utilities;
import com.disney.groovity.cache.Caches;
import com.disney.groovity.compile.CompilerConfigurationDecorator;
import com.disney.groovity.compile.GroovityASTTransformation;
import com.disney.groovity.compile.GroovityClassListener;
import com.disney.groovity.compile.GroovityClassLoader;
import com.disney.groovity.compile.GroovityCompilerEvent;
import com.disney.groovity.compile.GroovityCompilerEvent.Change;
import com.disney.groovity.compile.GroovitySourceTransformer;
import com.disney.groovity.compile.StatsASTTransformation;
import com.disney.groovity.compile.GroovitySourceTransformer.TransformedSource;
import com.disney.groovity.compile.SkipStatistics;
import com.disney.groovity.conf.Configurator;
import com.disney.groovity.conf.MultiConfigurator;
import com.disney.groovity.doc.Arg;
import com.disney.groovity.doc.Attr;
import com.disney.groovity.doc.ClassDescriptor;
import com.disney.groovity.doc.Function;
import com.disney.groovity.doc.Tag;
import com.disney.groovity.doc.ClassDescriptor.TypedName;
import com.disney.groovity.source.GroovitySource;
import com.disney.groovity.source.GroovitySourceListener;
import com.disney.groovity.source.GroovitySourceLocator;
import com.disney.groovity.stats.GroovityStatistics;
import com.disney.groovity.util.AsyncChannel;
import com.disney.groovity.util.ClosureWritable;
import com.disney.groovity.util.InterruptFactory;
import com.disney.groovity.util.ScriptHelper;
import com.disney.groovity.util.TypeLabel;
/**
* Groovity is the runtime engine for compiling, loading, running and visiting scripts. A Groovity is acquired using
* a GroovityBuilder which is used to define initialization parameters - Groovity itself only exposes runtime capabilities.
*
* Groovity handles compilation of sources from the configured source locators, the reading and writing of JAR files if so configured,
* the configuration, binding, argument resolution, loading and running of scripts, as well as visitation and notification of script
* changes for external frameworks
*
* Groovity is safe for multithreaded access, however it enforces single-threaded compilation, so calls to compile or compileAll
* should be prepared to handle an exception if concurrent compilation is attempted.
*
* @author Alex Vigdor
*
*/
public class Groovity implements GroovityConstants{
private static final Logger log = Logger.getLogger(Groovity.class.getName());
private static final String GROOVITY_SCRIPT_BINDING_PREFIX = INTERNAL_BINDING_PREFIX.concat("Groovity$script:");
private static final String GROOVITY_BINDING_DECORATED =INTERNAL_BINDING_PREFIX.concat("Groovity$bndDcr");
private static final List internalMethodNames = Arrays.asList(RUN,LOAD,TAG,STREAM,"methodMissing","propertyMissing","$static_propertyMissing","$static_methodMissing");
private static final Pattern sourcePattern = Pattern.compile("(?i)(/.*)\\".concat(GROOVITY_SOURCE_EXTENSION));
private static final Script PLACEHOLDER_SCRIPT = new Script() {
public Object run() {
return null;
}
};
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private final ConcurrentHashMap> scripts = new ConcurrentHashMap>();
private final ConcurrentHashMap scriptDates = new ConcurrentHashMap();
private final ConcurrentHashMap compileEvents = new ConcurrentHashMap();
private final ConcurrentHashMap> embeddedScripts = new ConcurrentHashMap>();
private File jarDirectory = null;
private ClassLoader parentLoader;
private List listeners = new ArrayList();
private GroovitySourceLocator[] sourceLocators;
private EnumSet sourcePhases;
private EnumSet jarPhases;
private String scriptBaseClass;
private Taggables tagLib;
private HttpClient httpClient;
private AtomicBoolean inCompile = new AtomicBoolean();
private int asyncThreads = Runtime.getRuntime().availableProcessors()*16;
private ExecutorService asyncExecutor;
private ScheduledExecutorService configExecutor;
private ScheduledExecutorService cacheRefreshExecutor;
private ScheduledExecutorService cacheTimeExecutor;
private InterruptFactory interruptFactory;
private boolean caseSensitive = true;
private BindingDecorator bindingDecorator;
private ArgsLookup argsLookup = null;
private GroovitySourceListener groovitySourceListener = new GroovitySourceListener() {
public void sourcesChanged(GroovitySource... sources) {
if(sourcePhases!=null && sourcePhases.contains(GroovityPhase.RUNTIME)){
try{
compile(false,true,sources);
}
catch(Error e){
log.log(Level.SEVERE,"Automatic compilation threw error",e);
}
}
}
};
private Configurator configurator;
@SuppressWarnings("rawtypes")
private ConcurrentHashMap traits = new ConcurrentHashMap<>();
private List compilerConfigurationDecorators;
private AtomicBoolean started = new AtomicBoolean(false);
//encourage use of the builder
protected Groovity(){
}
protected Script createScript(final String scriptName) throws InstantiationException, IllegalAccessException{
final Class