You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 00:56:36 UTC

[39/49] groovy git commit: Move source files to proper packages

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GrabExclude.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GrabExclude.java b/src/main/groovy/groovy/lang/GrabExclude.java
new file mode 100644
index 0000000..eb5a50d
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GrabExclude.java
@@ -0,0 +1,61 @@
+/*
+ *  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 groovy.lang;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to exclude an indirectly referenced artifact (a transitive dependency) from the classpath.
+ * <p>
+ * Examples:<br>
+ * {@code @GrabExclude(group='mysql', module='mysql-connector-java') // group/module form}<br>
+ * {@code @GrabExclude('mysql:mysql-connector-java') // compact form}<br>
+ * <p>
+ * Further information about customising grape behavior can be found on the Grape documentation page:
+ * <a href="http://groovy-lang.org/grape.html">http://groovy-lang.org/grape.html</a>.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.TYPE})
+public @interface GrabExclude {
+
+    /**
+     * The organisation or group, e.g.: "org.apache.ant"; required unless the compact form is used.
+     */
+    String group() default "";
+
+    /**
+     * The module or artifact, e.g.: "ant-junit"; required unless the compact form is used.
+     */
+    String module() default "";
+
+    /**
+     * Allows you to specify the group (organisation) and the module (artifact) in one of two compact convenience formats,
+     * e.g.: <code>@GrabExclude('org.apache.ant:ant-junit')</code> or <code>@GrabExclude('org.apache.ant#ant-junit')</code>
+     */
+    String value() default "";
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GrabResolver.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GrabResolver.java b/src/main/groovy/groovy/lang/GrabResolver.java
new file mode 100644
index 0000000..cf8d0ac
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GrabResolver.java
@@ -0,0 +1,86 @@
+/*
+ *  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 groovy.lang;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to add a repository for resolving Grape dependencies.
+ * <p>
+ * For example:
+ * <pre>
+ * {@code @GrabResolver}(name='restlet.org', root='http://maven.restlet.org')
+ * {@code @Grab}(group='org.restlet', module='org.restlet', version='1.1.6')
+ * class MyRestlet extends org.restlet.Restlet {
+ *   // ...
+ * }
+ * </pre>
+ * By default, the Grapes subsystem uses an Ivy chained resolver. Each resolver
+ * added using {@code @GrabResolver} is appended to the chain. By default, the grape
+ * subsystem is shared globally, so added resolvers will become available for any subsequent
+ * grab calls. Dependency resolution follows Ivy's artifact resolution which tries
+ * to resolve artifacts in the order specified in the chain of resolvers.
+ * <p>
+ * Further information about customising grape behavior can be found on the Grape documentation page:
+ * <a href="http://groovy-lang.org/grape.html">http://groovy-lang.org/grape.html</a>.
+ *
+ * @author Merlyn Albery-Speyer
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.TYPE})
+public @interface GrabResolver {
+    /**
+     * Allows a shorthand form which sets the name and root to this value.
+     * Must not be used if name() or root() is non-empty.
+     */
+    String value() default "";
+
+    /**
+     * A meaningful name for a repo containing the grape/artifact.
+     * A non-empty value is required unless value() is used.
+     */
+    String name() default "";
+
+    /**
+     * The URL for a repo containing the grape/artifact.
+     * A non-empty value is required unless value() is used.
+     */
+    String root() default "";
+
+    /**
+     * Defaults to Maven2 compatibility. Set false for Ivy only compatibility.
+     */
+    boolean m2Compatible() default true;
+
+    /**
+     * By default, when a {@code @GrabResolver} annotation is used, a {@code Grape.addResolver()} call is added
+     * to the static initializers of the class the annotatable node appears in.
+     * If you wish to disable this, add {@code initClass=false} to the annotation.
+     */
+    boolean initClass() default true;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Grapes.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Grapes.java b/src/main/groovy/groovy/lang/Grapes.java
new file mode 100644
index 0000000..8719c74
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Grapes.java
@@ -0,0 +1,71 @@
+/*
+ *  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 groovy.lang;
+
+/**
+ * Sometimes we will need more than one grab per class, but we can only add
+ * one annotation type per annotatable node.  This class allows for multiple
+ * grabs to be added.
+ * <p>
+ * For example:
+ * <p>
+ * <pre>
+ * {@code @Grapes([@Grab(module='m1'), @Grab(module='m2')])}
+ * class AnnotatedClass { ... }
+ * </pre>
+ * <p>
+ * You can override an implicit transitive dependency by providing an explicit one.
+ * E.g. htmlunit 2.6 normally uses xerces 2.9.1 but you can get 2.9.0 as follows:
+ * <pre>
+ * {@code @Grapes}([
+ *     {@code @Grab}('net.sourceforge.htmlunit:htmlunit:2.6'),
+ *     {@code @Grab}('xerces#xercesImpl;2.9.0')
+ * ])
+ * </pre>
+ * Obviously, only do this if you understand the consequences.
+ * <p>
+ * You can also remove transitive dependencies altogether (provided you
+ * know you don't need them) using {@code @GrabExclude}.
+ * For example, here is how we would not grab the {@code logkit} and
+ * {@code avalon-framework} transitive dependencies for Apache POI:
+ * <pre>
+ * {@code @Grapes}([
+ *   {@code @Grab}("org.apache.poi#poi;3.5-beta6"),
+ *   {@code @GrabExclude}("logkit:logkit"),
+ *   {@code @GrabExclude}("avalon-framework#avalon-framework")
+ * ])
+ * import org.apache.poi.hssf.util.CellReference
+ * assert new CellReference(0, 0, false, false).formatAsString() == 'A1'
+ * assert new CellReference(1, 3).formatAsString() == '$D$2'
+ * </pre>
+ * It is also sometimes also useful to use {@code @GrabConfig} to further adjust how dependencies
+ * are grabbed. See {@code @GrabConfig} for further information.
+ */
+public @interface Grapes {
+    Grab[] value();
+
+    /**
+     * This will be pushed into the child grab annotations if the value is not
+     * set in the child annotation already.
+     * <p>
+     * This results in an effective change in the default value, which each &#064;Grab
+     * can still override
+     */
+    boolean initClass() default true;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyCallable.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyCallable.java b/src/main/groovy/groovy/lang/GroovyCallable.java
new file mode 100644
index 0000000..e425c92
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyCallable.java
@@ -0,0 +1,32 @@
+/*
+ *  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 groovy.lang;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A special "marker" style interface allowing Groovy classes to implement both
+ * Runnable and Callable yet give preference to Runnable (for backwards compatibility)
+ * for APIs having both Runnable and Callable methods. You should generally NOT use this
+ * method in your own code.
+ *
+ * @see java.util.concurrent.Callable
+ * @since 1.8.0
+ */
+public interface GroovyCallable<V> extends Callable<V> { }

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyClassLoader.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyClassLoader.java b/src/main/groovy/groovy/lang/GroovyClassLoader.java
new file mode 100644
index 0000000..8581879
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyClassLoader.java
@@ -0,0 +1,1099 @@
+/*
+ *  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.
+ */
+/*
+ * @todo multi threaded compiling of the same class but with different roots
+ * for compilation... T1 compiles A, which uses B, T2 compiles B... mark A and B
+ * as parsed and then synchronize compilation. Problems: How to synchronize?
+ * How to get error messages?
+ *
+ */
+package groovy.lang;
+
+import groovy.util.CharsetToolkit;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.BytecodeProcessor;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.IOGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache;
+import org.codehaus.groovy.runtime.memoize.EvictableCache;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+ * A ClassLoader which can load Groovy classes. The loaded classes are cached,
+ * classes from other classloaders should not be cached. To be able to load a
+ * script that was asked for earlier but was created later it is essential not
+ * to keep anything like a "class not found" information for that class name.
+ * This includes possible parent loaders. Classes that are not cached are always
+ * reloaded.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @author Steve Goetze
+ * @author Bing Ran
+ * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ */
+public class GroovyClassLoader extends URLClassLoader {
+    private static final URL[] EMPTY_URL_ARRAY = new URL[0];
+    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+    /**
+     * this cache contains the loaded classes or PARSING, if the class is currently parsed
+     */
+    protected final EvictableCache<String, Class> classCache = new ConcurrentCommonCache<String, Class>();
+
+    /**
+     * This cache contains mappings of file name to class. It is used
+     * to bypass compilation.
+     */
+    protected final ConcurrentCommonCache<String, Class> sourceCache = new ConcurrentCommonCache<String, Class>();
+
+    private final CompilerConfiguration config;
+    private String sourceEncoding;
+    private Boolean recompile;
+    // use 1000000 as offset to avoid conflicts with names form the GroovyShell
+    private static int scriptNameCounter = 1000000;
+
+    private GroovyResourceLoader resourceLoader = new GroovyResourceLoader() {
+        public URL loadGroovySource(final String filename) throws MalformedURLException {
+            return AccessController.doPrivileged(new PrivilegedAction<URL>() {
+                public URL run() {
+                    for (String extension : config.getScriptExtensions()) {
+                        try {
+                            URL ret = getSourceFile(filename, extension);
+                            if (ret != null)
+                                return ret;
+                        } catch (Throwable t) { //
+                        }
+                    }
+                    return null;
+                }
+            });
+        }
+    };
+
+    /**
+     * creates a GroovyClassLoader using the current Thread's context
+     * Class loader as parent.
+     */
+    public GroovyClassLoader() {
+        this(Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given ClassLoader as parent
+     */
+    public GroovyClassLoader(ClassLoader loader) {
+        this(loader, null);
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given GroovyClassLoader as parent.
+     * This loader will get the parent's CompilerConfiguration
+     */
+    public GroovyClassLoader(GroovyClassLoader parent) {
+        this(parent, parent.config, false);
+    }
+
+    /**
+     * creates a GroovyClassLoader.
+     *
+     * @param parent                    the parent class loader
+     * @param config                    the compiler configuration
+     * @param useConfigurationClasspath determines if the configurations classpath should be added
+     */
+    public GroovyClassLoader(ClassLoader parent, CompilerConfiguration config, boolean useConfigurationClasspath) {
+        super(EMPTY_URL_ARRAY, parent);
+        if (config == null) config = CompilerConfiguration.DEFAULT;
+        this.config = config;
+        if (useConfigurationClasspath) {
+            for (String path : config.getClasspath()) {
+                this.addClasspath(path);
+            }
+        }
+
+        initSourceEncoding(config);
+    }
+
+    private void initSourceEncoding(CompilerConfiguration config) {
+        sourceEncoding = config.getSourceEncoding();
+        if (null ==  sourceEncoding) {
+            // Keep the same default source encoding with the one used by #parseClass(InputStream, String)
+            // TODO should we use org.codehaus.groovy.control.CompilerConfiguration.DEFAULT_SOURCE_ENCODING instead?
+            sourceEncoding = CharsetToolkit.getDefaultSystemCharset().name();
+        }
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given ClassLoader as parent.
+     */
+    public GroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
+        this(loader, config, true);
+    }
+
+    public void setResourceLoader(GroovyResourceLoader resourceLoader) {
+        if (resourceLoader == null) {
+            throw new IllegalArgumentException("Resource loader must not be null!");
+        }
+        this.resourceLoader = resourceLoader;
+    }
+
+    public GroovyResourceLoader getResourceLoader() {
+        return resourceLoader;
+    }
+
+    /**
+     * Loads the given class node returning the implementation Class.
+     * <p>
+     * WARNING: this compilation is not synchronized
+     *
+     * @param classNode
+     * @return a class
+     */
+    public Class defineClass(ClassNode classNode, String file, String newCodeBase) {
+        CodeSource codeSource = null;
+        try {
+            codeSource = new CodeSource(new URL("file", "", newCodeBase), (java.security.cert.Certificate[]) null);
+        } catch (MalformedURLException e) {
+            //swallow
+        }
+
+        CompilationUnit unit = createCompilationUnit(config, codeSource);
+        ClassCollector collector = createCollector(unit, classNode.getModule().getContext());
+        try {
+            unit.addClassNode(classNode);
+            unit.setClassgenCallback(collector);
+            unit.compile(Phases.CLASS_GENERATION);
+            definePackageInternal(collector.generatedClass.getName());
+            return collector.generatedClass;
+        } catch (CompilationFailedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Parses the given file into a Java class capable of being run
+     *
+     * @param file the file name to parse
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(File file) throws CompilationFailedException, IOException {
+        return parseClass(new GroovyCodeSource(file, config.getSourceEncoding()));
+    }
+
+    /**
+     * Parses the given text into a Java class capable of being run
+     *
+     * @param text     the text of the script/class to parse
+     * @param fileName the file name to use as the name of the class
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(final String text, final String fileName) throws CompilationFailedException {
+        GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+            public GroovyCodeSource run() {
+                return new GroovyCodeSource(text, fileName, "/groovy/script");
+            }
+        });
+        gcs.setCachable(false);
+        return parseClass(gcs);
+    }
+
+    /**
+     * Parses the given text into a Java class capable of being run
+     *
+     * @param text the text of the script/class to parse
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(String text) throws CompilationFailedException {
+        return parseClass(text, "script" + System.currentTimeMillis() +
+                Math.abs(text.hashCode()) + ".groovy");
+    }
+
+    public synchronized String generateScriptName() {
+        scriptNameCounter++;
+        return "script" + scriptNameCounter + ".groovy";
+    }
+
+    public Class parseClass(final Reader reader, final String fileName) throws CompilationFailedException {
+        GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+            public GroovyCodeSource run() {
+                try {
+                    String scriptText = IOGroovyMethods.getText(reader);
+                    return new GroovyCodeSource(scriptText, fileName, "/groovy/script");
+                } catch (IOException e) {
+                    throw new RuntimeException("Impossible to read the content of the reader for file named: " + fileName, e);
+                }
+            }
+        });
+        return parseClass(gcs);
+    }
+    
+    /**
+     * @deprecated Prefer using methods taking a Reader rather than an InputStream to avoid wrong encoding issues.
+     * Use {@link #parseClass(Reader, String) parseClass} instead
+     */
+    @Deprecated
+    public Class parseClass(final InputStream in, final String fileName) throws CompilationFailedException {
+        // For generic input streams, provide a catch-all codebase of GroovyScript
+        // Security for these classes can be administered via policy grants with
+        // a codebase of file:groovy.script
+        GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+            public GroovyCodeSource run() {
+                try {
+                    String scriptText = config.getSourceEncoding() != null ?
+                            IOGroovyMethods.getText(in, config.getSourceEncoding()) :
+                            IOGroovyMethods.getText(in);
+                    return new GroovyCodeSource(scriptText, fileName, "/groovy/script");
+                } catch (IOException e) {
+                    throw new RuntimeException("Impossible to read the content of the input stream for file named: " + fileName, e);
+                }
+            }
+        });
+        return parseClass(gcs);
+    }
+
+    public Class parseClass(GroovyCodeSource codeSource) throws CompilationFailedException {
+        return parseClass(codeSource, codeSource.isCachable());
+    }
+
+    /**
+     * Parses the given code source into a Java class. If there is a class file
+     * for the given code source, then no parsing is done, instead the cached class is returned.
+     *
+     * @param shouldCacheSource if true then the generated class will be stored in the source cache
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(final GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException {
+        return sourceCache.getAndPut(
+                codeSource.getName(),
+                new EvictableCache.ValueProvider<String, Class>() {
+                    @Override
+                    public Class provide(String key) {
+                        return doParseClass(codeSource);
+                    }
+                },
+                shouldCacheSource
+        );
+    }
+
+    private Class doParseClass(GroovyCodeSource codeSource) {
+        validate(codeSource);
+        Class answer;  // Was neither already loaded nor compiling, so compile and add to cache.
+        CompilationUnit unit = createCompilationUnit(config, codeSource.getCodeSource());
+        if (recompile!=null && recompile || recompile==null && config.getRecompileGroovySource()) {
+            unit.addFirstPhaseOperation(TimestampAdder.INSTANCE, CompilePhase.CLASS_GENERATION.getPhaseNumber());
+        }
+        SourceUnit su = null;
+        File file = codeSource.getFile();
+        if (file != null) {
+            su = unit.addSource(file);
+        } else {
+            URL url = codeSource.getURL();
+            if (url != null) {
+                su = unit.addSource(url);
+            } else {
+                su = unit.addSource(codeSource.getName(), codeSource.getScriptText());
+            }
+        }
+
+        ClassCollector collector = createCollector(unit, su);
+        unit.setClassgenCallback(collector);
+        int goalPhase = Phases.CLASS_GENERATION;
+        if (config != null && config.getTargetDirectory() != null) goalPhase = Phases.OUTPUT;
+        unit.compile(goalPhase);
+
+        answer = collector.generatedClass;
+        String mainClass = su.getAST().getMainClassName();
+        for (Object o : collector.getLoadedClasses()) {
+            Class clazz = (Class) o;
+            String clazzName = clazz.getName();
+            definePackageInternal(clazzName);
+            setClassCacheEntry(clazz);
+            if (clazzName.equals(mainClass)) answer = clazz;
+        }
+        return answer;
+    }
+
+    private static void validate(GroovyCodeSource codeSource) {
+        if (codeSource.getFile() == null) {
+            if (codeSource.getScriptText() == null) {
+                throw new IllegalArgumentException("Script text to compile cannot be null!");
+            }
+        }
+    }
+
+    private void definePackageInternal(String className) {
+        int i = className.lastIndexOf('.');
+        if (i != -1) {
+            String pkgName = className.substring(0, i);
+            java.lang.Package pkg = getPackage(pkgName);
+            if (pkg == null) {
+                definePackage(pkgName, null, null, null, null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     * gets the currently used classpath.
+     *
+     * @return a String[] containing the file information of the urls
+     * @see #getURLs()
+     */
+    protected String[] getClassPath() {
+        //workaround for Groovy-835
+        URL[] urls = getURLs();
+        String[] ret = new String[urls.length];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = urls[i].getFile();
+        }
+        return ret;
+    }
+
+    protected PermissionCollection getPermissions(CodeSource codeSource) {
+        PermissionCollection perms;
+        try {
+            try {
+                perms = super.getPermissions(codeSource);
+            } catch (SecurityException e) {
+                // We lied about our CodeSource and that makes URLClassLoader unhappy.
+                perms = new Permissions();
+            }
+
+            ProtectionDomain myDomain = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
+                public ProtectionDomain run() {
+                    return getClass().getProtectionDomain();
+                }
+            });
+            PermissionCollection myPerms = myDomain.getPermissions();
+            if (myPerms != null) {
+                for (Enumeration<Permission> elements = myPerms.elements(); elements.hasMoreElements();) {
+                    perms.add(elements.nextElement());
+                }
+            }
+        } catch (Throwable e) {
+            // We lied about our CodeSource and that makes URLClassLoader unhappy.
+            perms = new Permissions();
+        }
+        perms.setReadOnly();
+        return perms;
+    }
+
+    public static class InnerLoader extends GroovyClassLoader {
+        private final GroovyClassLoader delegate;
+        private final long timeStamp;
+
+        public InnerLoader(GroovyClassLoader delegate) {
+            super(delegate);
+            this.delegate = delegate;
+            timeStamp = System.currentTimeMillis();
+        }
+
+        public void addClasspath(String path) {
+            delegate.addClasspath(path);
+        }
+
+        public void clearCache() {
+            delegate.clearCache();
+        }
+
+        public URL findResource(String name) {
+            return delegate.findResource(name);
+        }
+
+        public Enumeration findResources(String name) throws IOException {
+            return delegate.findResources(name);
+        }
+
+        public Class[] getLoadedClasses() {
+            return delegate.getLoadedClasses();
+        }
+
+        public URL getResource(String name) {
+            return delegate.getResource(name);
+        }
+
+        public InputStream getResourceAsStream(String name) {
+            return delegate.getResourceAsStream(name);
+        }
+
+        public GroovyResourceLoader getResourceLoader() {
+            return delegate.getResourceLoader();
+        }
+
+        public URL[] getURLs() {
+            return delegate.getURLs();
+        }
+
+        public Class loadClass(String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve) throws ClassNotFoundException, CompilationFailedException {
+            Class c = findLoadedClass(name);
+            if (c != null) return c;
+            return delegate.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve);
+        }
+
+        public Class parseClass(GroovyCodeSource codeSource, boolean shouldCache) throws CompilationFailedException {
+            return delegate.parseClass(codeSource, shouldCache);
+        }
+
+        public void setResourceLoader(GroovyResourceLoader resourceLoader) {
+            delegate.setResourceLoader(resourceLoader);
+        }
+
+        public void addURL(URL url) {
+            delegate.addURL(url);
+        }
+
+        public long getTimeStamp() {
+            return timeStamp;
+        }
+    }
+
+    /**
+     * creates a new CompilationUnit. If you want to add additional
+     * phase operations to the CompilationUnit (for example to inject
+     * additional methods, variables, fields), then you should overwrite
+     * this method.
+     *
+     * @param config the compiler configuration, usually the same as for this class loader
+     * @param source the source containing the initial file to compile, more files may follow during compilation
+     * @return the CompilationUnit
+     */
+    protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
+        return new CompilationUnit(config, source, this);
+    }
+
+    /**
+     * creates a ClassCollector for a new compilation.
+     *
+     * @param unit the compilationUnit
+     * @param su   the SourceUnit
+     * @return the ClassCollector
+     */
+    protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
+        InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() {
+            public InnerLoader run() {
+                return new InnerLoader(GroovyClassLoader.this);
+            }
+        });
+        return new ClassCollector(loader, unit, su);
+    }
+
+    public static class ClassCollector extends CompilationUnit.ClassgenCallback {
+        private Class generatedClass;
+        private final GroovyClassLoader cl;
+        private final SourceUnit su;
+        private final CompilationUnit unit;
+        private final Collection<Class> loadedClasses;
+
+        protected ClassCollector(InnerLoader cl, CompilationUnit unit, SourceUnit su) {
+            this.cl = cl;
+            this.unit = unit;
+            this.loadedClasses = new ArrayList<Class>();
+            this.su = su;
+        }
+
+        public GroovyClassLoader getDefiningClassLoader() {
+            return cl;
+        }
+
+        protected Class createClass(byte[] code, ClassNode classNode) {
+            BytecodeProcessor bytecodePostprocessor = unit.getConfiguration().getBytecodePostprocessor();
+            byte[] fcode = code;
+            if (bytecodePostprocessor!=null) {
+                fcode = bytecodePostprocessor.processBytecode(classNode.getName(), fcode);
+            }
+            GroovyClassLoader cl = getDefiningClassLoader();
+            Class theClass = cl.defineClass(classNode.getName(), fcode, 0, fcode.length, unit.getAST().getCodeSource());
+            this.loadedClasses.add(theClass);
+
+            if (generatedClass == null) {
+                ModuleNode mn = classNode.getModule();
+                SourceUnit msu = null;
+                if (mn != null) msu = mn.getContext();
+                ClassNode main = null;
+                if (mn != null) main = (ClassNode) mn.getClasses().get(0);
+                if (msu == su && main == classNode) generatedClass = theClass;
+            }
+
+            return theClass;
+        }
+
+        protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) {
+            byte[] code = classWriter.toByteArray();
+            return createClass(code, classNode);
+        }
+
+        public void call(ClassVisitor classWriter, ClassNode classNode) {
+            onClassNode((ClassWriter) classWriter, classNode);
+        }
+
+        public Collection getLoadedClasses() {
+            return this.loadedClasses;
+        }
+    }
+
+    /**
+     * open up the super class define that takes raw bytes
+     */
+    public Class defineClass(String name, byte[] b) {
+        return super.defineClass(name, b, 0, b.length);
+    }
+
+    /**
+     * loads a class from a file or a parent classloader.
+     * This method does call loadClass(String, boolean, boolean, boolean)
+     * with the last parameter set to false.
+     *
+     * @throws CompilationFailedException if compilation was not successful
+     */
+    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript)
+            throws ClassNotFoundException, CompilationFailedException {
+        return loadClass(name, lookupScriptFiles, preferClassOverScript, false);
+    }
+
+    /**
+     * gets a class from the class cache. This cache contains only classes loaded through
+     * this class loader or an InnerLoader instance. If no class is stored for a
+     * specific name, then the method should return null.
+     *
+     * @param name of the class
+     * @return the class stored for the given name
+     * @see #removeClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #clearCache()
+     */
+    protected Class getClassCacheEntry(String name) {
+        return classCache.get(name);
+    }
+
+    /**
+     * sets an entry in the class cache.
+     *
+     * @param cls the class
+     * @see #removeClassCacheEntry(String)
+     * @see #getClassCacheEntry(String)
+     * @see #clearCache()
+     */
+    protected void setClassCacheEntry(Class cls) {
+        classCache.put(cls.getName(), cls);
+    }
+
+    /**
+     * removes a class from the class cache.
+     *
+     * @param name of the class
+     * @see #getClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #clearCache()
+     */
+    protected void removeClassCacheEntry(String name) {
+        classCache.remove(name);
+    }
+
+    /**
+     * adds a URL to the classloader.
+     *
+     * @param url the new classpath element
+     */
+    public void addURL(URL url) {
+        super.addURL(url);
+    }
+
+    /**
+     * Indicates if a class is recompilable. Recompilable means, that the classloader
+     * will try to locate a groovy source file for this class and then compile it again,
+     * adding the resulting class as entry to the cache. Giving null as class is like a
+     * recompilation, so the method should always return true here. Only classes that are
+     * implementing GroovyObject are compilable and only if the timestamp in the class
+     * is lower than Long.MAX_VALUE.
+     * <p>
+     * NOTE: First the parent loaders will be asked and only if they don't return a
+     * class the recompilation will happen. Recompilation also only happen if the source
+     * file is newer.
+     *
+     * @param cls the class to be tested. If null the method should return true
+     * @return true if the class should be compiled again
+     * @see #isSourceNewer(URL, Class)
+     */
+    protected boolean isRecompilable(Class cls) {
+        if (cls == null) return true;
+        if (cls.getClassLoader() == this) return false;
+        if (recompile == null && !config.getRecompileGroovySource()) return false;
+        if (recompile != null && !recompile) return false;
+        if (!GroovyObject.class.isAssignableFrom(cls)) return false;
+        long timestamp = getTimeStamp(cls);
+        if (timestamp == Long.MAX_VALUE) return false;
+        return true;
+    }
+
+    /**
+     * sets if the recompilation should be enable. There are 3 possible
+     * values for this. Any value different than null overrides the
+     * value from the compiler configuration. true means to recompile if needed
+     * false means to never recompile.
+     *
+     * @param mode the recompilation mode
+     * @see CompilerConfiguration
+     */
+    public void setShouldRecompile(Boolean mode) {
+        recompile = mode;
+    }
+
+    /**
+     * gets the currently set recompilation mode. null means, the
+     * compiler configuration is used. False means no recompilation and
+     * true means that recompilation will be done if needed.
+     *
+     * @return the recompilation mode
+     */
+    public Boolean isShouldRecompile() {
+        return recompile;
+    }
+
+    /**
+     * loads a class from a file or a parent classloader.
+     *
+     * @param name                  of the class to be loaded
+     * @param lookupScriptFiles     if false no lookup at files is done at all
+     * @param preferClassOverScript if true the file lookup is only done if there is no class
+     * @param resolve               see {@link java.lang.ClassLoader#loadClass(java.lang.String, boolean)}
+     * @return the class found or the class created from a file lookup
+     * @throws ClassNotFoundException     if the class could not be found
+     * @throws CompilationFailedException if the source file could not be compiled
+     */
+    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve)
+            throws ClassNotFoundException, CompilationFailedException {
+        // look into cache
+        Class cls = getClassCacheEntry(name);
+
+        // enable recompilation?
+        boolean recompile = isRecompilable(cls);
+        if (!recompile) return cls;
+
+        // try parent loader
+        ClassNotFoundException last = null;
+        try {
+            Class parentClassLoaderClass = super.loadClass(name, resolve);
+            // always return if the parent loader was successful
+            if (cls != parentClassLoaderClass) return parentClassLoaderClass;
+        } catch (ClassNotFoundException cnfe) {
+            last = cnfe;
+        } catch (NoClassDefFoundError ncdfe) {
+            if (ncdfe.getMessage().indexOf("wrong name") > 0) {
+                last = new ClassNotFoundException(name);
+            } else {
+                throw ncdfe;
+            }
+        }
+
+        // check security manager
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            String className = name.replace('/', '.');
+            int i = className.lastIndexOf('.');
+            // no checks on the sun.reflect classes for reflection speed-up
+            // in particular ConstructorAccessorImpl, MethodAccessorImpl, FieldAccessorImpl and SerializationConstructorAccessorImpl
+            // which are generated at runtime by the JDK
+            if (i != -1 && !className.startsWith("sun.reflect.")) {
+                sm.checkPackageAccess(className.substring(0, i));
+            }
+        }
+
+        // prefer class if no recompilation
+        if (cls != null && preferClassOverScript) return cls;
+
+        // at this point the loading from a parent loader failed
+        // and we want to recompile if needed.
+        if (lookupScriptFiles) {
+            // try groovy file
+            try {
+                // check if recompilation already happened.
+                final Class classCacheEntry = getClassCacheEntry(name);
+                if (classCacheEntry != cls) return classCacheEntry;
+                URL source = resourceLoader.loadGroovySource(name);
+                // if recompilation fails, we want cls==null
+                Class oldClass = cls;
+                cls = null;
+                cls = recompile(source, name, oldClass);
+            } catch (IOException ioe) {
+                last = new ClassNotFoundException("IOException while opening groovy source: " + name, ioe);
+            } finally {
+                if (cls == null) {
+                    removeClassCacheEntry(name);
+                } else {
+                    setClassCacheEntry(cls);
+                }
+            }
+        }
+
+        if (cls == null) {
+            // no class found, there should have been an exception before now
+            if (last == null) throw new AssertionError(true);
+            throw last;
+        }
+        return cls;
+    }
+
+    /**
+     * (Re)Compiles the given source.
+     * This method starts the compilation of a given source, if
+     * the source has changed since the class was created. For
+     * this isSourceNewer is called.
+     *
+     * @param source    the source pointer for the compilation
+     * @param className the name of the class to be generated
+     * @param oldClass  a possible former class
+     * @return the old class if the source wasn't new enough, the new class else
+     * @throws CompilationFailedException if the compilation failed
+     * @throws IOException                if the source is not readable
+     * @see #isSourceNewer(URL, Class)
+     */
+    protected Class recompile(URL source, String className, Class oldClass) throws CompilationFailedException, IOException {
+        if (source != null) {
+            // found a source, compile it if newer
+            if ((oldClass != null && isSourceNewer(source, oldClass)) || (oldClass == null)) {
+                String name = source.toExternalForm();
+
+                sourceCache.remove(name);
+
+                if (isFile(source)) {
+                    try {
+                        return parseClass(new GroovyCodeSource(new File(source.toURI()), sourceEncoding));
+                    } catch (URISyntaxException e) {
+                        // do nothing and fall back to the other version
+                    }
+                }
+                return parseClass(new InputStreamReader(source.openStream(), sourceEncoding), name);
+            }
+        }
+        return oldClass;
+    }
+
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        return loadClass(name, false);
+    }
+
+    /**
+     * Implemented here to check package access prior to returning an
+     * already loaded class.
+     *
+     * @throws CompilationFailedException if the compilation failed
+     * @throws ClassNotFoundException     if the class was not found
+     * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
+     */
+    protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
+        return loadClass(name, true, true, resolve);
+    }
+
+    /**
+     * gets the time stamp of a given class. For groovy
+     * generated classes this usually means to return the value
+     * of the static field __timeStamp. If the parameter doesn't
+     * have such a field, then Long.MAX_VALUE is returned
+     *
+     * @param cls the class
+     * @return the time stamp
+     */
+    protected long getTimeStamp(Class cls) {
+        return Verifier.getTimestamp(cls);
+    }
+
+    /**
+     * This method will take a file name and try to "decode" any URL encoded characters.  For example
+     * if the file name contains any spaces this method call will take the resulting %20 encoded values
+     * and convert them to spaces.
+     * <p>
+     * This method was added specifically to fix defect:  Groovy-1787.  The defect involved a situation
+     * where two scripts were sitting in a directory with spaces in its name.  The code would fail
+     * when the class loader tried to resolve the file name and would choke on the URLEncoded space values.
+     */
+    private static String decodeFileName(String fileName) {
+        String decodedFile = fileName;
+        try {
+            decodedFile = URLDecoder.decode(fileName, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            System.err.println("Encountered an invalid encoding scheme when trying to use URLDecoder.decode() inside of the GroovyClassLoader.decodeFileName() method.  Returning the unencoded URL.");
+            System.err.println("Please note that if you encounter this error and you have spaces in your directory you will run into issues.  Refer to GROOVY-1787 for description of this bug.");
+        }
+
+        return decodedFile;
+    }
+
+    private static boolean isFile(URL ret) {
+        return ret != null && ret.getProtocol().equals("file");
+    }
+
+    private static File getFileForUrl(URL ret, String filename) {
+        String fileWithoutPackage = filename;
+        if (fileWithoutPackage.indexOf('/') != -1) {
+            int index = fileWithoutPackage.lastIndexOf('/');
+            fileWithoutPackage = fileWithoutPackage.substring(index + 1);
+        }
+        return fileReallyExists(ret, fileWithoutPackage);
+    }
+
+    private static File fileReallyExists(URL ret, String fileWithoutPackage) {
+        File path;
+        try {
+            /* fix for GROOVY-5809 */ 
+            path = new File(ret.toURI());
+        } catch(URISyntaxException e) {
+            path = new File(decodeFileName(ret.getFile()));
+        }
+        path = path.getParentFile();
+        if (path.exists() && path.isDirectory()) {
+            File file = new File(path, fileWithoutPackage);
+            if (file.exists()) {
+                // file.exists() might be case insensitive. Let's do
+                // case sensitive match for the filename
+                File parent = file.getParentFile();
+                for (String child : parent.list()) {
+                    if (child.equals(fileWithoutPackage)) return file;
+                }
+            }
+        }
+        //file does not exist!
+        return null;
+    }
+
+    private URL getSourceFile(String name, String extension) {
+        String filename = name.replace('.', '/') + "." + extension;
+        URL ret = getResource(filename);
+        if (isFile(ret) && getFileForUrl(ret, filename) == null) return null;
+        return ret;
+    }
+
+    /**
+     * Decides if the given source is newer than a class.
+     *
+     * @param source the source we may want to compile
+     * @param cls    the former class
+     * @return true if the source is newer, false else
+     * @throws IOException if it is not possible to open an
+     *                     connection for the given source
+     * @see #getTimeStamp(Class)
+     */
+    protected boolean isSourceNewer(URL source, Class cls) throws IOException {
+        long lastMod;
+
+        // Special handling for file:// protocol, as getLastModified() often reports
+        // incorrect results (-1)
+        if (isFile(source)) {
+            // Coerce the file URL to a File
+            // See ClassNodeResolver.isSourceNewer for another method that replaces '|' with ':'.
+            // WTF: Why is this done and where is it documented?
+            String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
+            File file = new File(path);
+            lastMod = file.lastModified();
+        } else {
+            URLConnection conn = source.openConnection();
+            lastMod = conn.getLastModified();
+            conn.getInputStream().close();
+        }
+        long classTime = getTimeStamp(cls);
+        return classTime + config.getMinimumRecompilationInterval() < lastMod;
+    }
+
+    /**
+     * adds a classpath to this classloader.
+     *
+     * @param path is a jar file or a directory.
+     * @see #addURL(URL)
+     */
+    public void addClasspath(final String path) {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            public Void run() {
+
+                URI newURI;
+                try {
+                    newURI = new URI(path);
+                    // check if we can create a URL from that URI
+                    newURI.toURL();
+                } catch (URISyntaxException e) {
+                    // the URI has a false format, so lets try it with files ...
+                    newURI=new File(path).toURI();
+                } catch (MalformedURLException e) {
+                    // the URL has a false format, so lets try it with files ...
+                    newURI=new File(path).toURI();
+                } catch (IllegalArgumentException e) {
+                    // the URL is not absolute, so lets try it with files ...
+                    newURI=new File(path).toURI();
+                }
+
+                URL[] urls = getURLs();
+                for (URL url : urls) {
+                    // Do not use URL.equals.  It uses the network to resolve names and compares ip addresses!
+                    // That is a violation of RFC and just plain evil.
+                    // http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html
+                    // http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#equals(java.lang.Object)
+                    // "Since hosts comparison requires name resolution, this operation is a blocking operation.
+                    // Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP."
+                    try {
+                        if (newURI.equals(url.toURI())) return null;
+                    } catch (URISyntaxException e) {
+                        // fail fast! if we got a malformed URI the Classloader has to tell it
+                        throw new RuntimeException( e );
+                    }
+                }
+                try {
+                    addURL(newURI.toURL());
+                } catch (MalformedURLException e) {
+                    // fail fast! if we got a malformed URL the Classloader has to tell it
+                    throw new RuntimeException( e );
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * <p>Returns all Groovy classes loaded by this class loader.
+     *
+     * @return all classes loaded by this class loader
+     */
+    public Class[] getLoadedClasses() {
+        return classCache.values().toArray(EMPTY_CLASS_ARRAY);
+    }
+
+    /**
+     * Removes all classes from the class cache.
+     * <p>
+     * In addition to internal caches this method also clears any
+     * previously set MetaClass information for the given set of
+     * classes being removed.
+     *
+     * @see #getClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #removeClassCacheEntry(String)
+     */
+    public void clearCache() {
+        Map<String, Class> clearedClasses = classCache.clear();
+
+        sourceCache.clear();
+
+        for (Map.Entry<String, Class> entry : clearedClasses.entrySet()) {
+            // Another Thread may be using an instance of this class
+            // (for the first time) requiring a ClassInfo lock and
+            // classloading which would require a lock on classCache.
+            // The following locks on ClassInfo and to avoid deadlock
+            // should not be done with a classCache lock.
+            InvokerHelper.removeClass(entry.getValue());
+        }
+    }
+
+    /**
+     * Closes this GroovyClassLoader and clears any caches it maintains.
+     * <p>
+     * No use should be made of this instance after this method is
+     * invoked. Any classes that are already loaded are still accessible.
+     *
+     * @throws IOException
+     * @see URLClassLoader#close()
+     * @see #clearCache()
+     * @since 2.5.0
+     */
+    @Override
+    public void close() throws IOException {
+        super.close();
+        clearCache();
+    }
+
+    private static class TimestampAdder extends CompilationUnit.PrimaryClassNodeOperation implements Opcodes {
+        private static final TimestampAdder INSTANCE = new TimestampAdder();
+
+        private TimestampAdder() {}
+
+        protected void addTimeStamp(ClassNode node) {
+            if (node.getDeclaredField(Verifier.__TIMESTAMP) == null) { // in case if verifier visited the call already
+                FieldNode timeTagField = new FieldNode(
+                        Verifier.__TIMESTAMP,
+                        ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC,
+                        ClassHelper.long_TYPE,
+                        //"",
+                        node,
+                        new ConstantExpression(System.currentTimeMillis()));
+                // alternatively, FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
+                timeTagField.setSynthetic(true);
+                node.addField(timeTagField);
+
+                timeTagField = new FieldNode(
+                        Verifier.__TIMESTAMP__ + String.valueOf(System.currentTimeMillis()),
+                        ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC,
+                        ClassHelper.long_TYPE,
+                        //"",
+                        node,
+                        new ConstantExpression((long) 0));
+                // alternatively, FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
+                timeTagField.setSynthetic(true);
+                node.addField(timeTagField);
+            }
+        }
+
+        @Override
+        public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException {
+            if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) > 0) {
+                // does not apply on interfaces
+                return;
+            }
+            if (!(classNode instanceof InnerClassNode)) {
+                addTimeStamp(classNode);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyCodeSource.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyCodeSource.java b/src/main/groovy/groovy/lang/GroovyCodeSource.java
new file mode 100644
index 0000000..a5a2bb1
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyCodeSource.java
@@ -0,0 +1,266 @@
+/*
+ *  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 groovy.lang;
+
+import groovy.security.GroovyCodeSourcePermission;
+import groovy.util.CharsetToolkit;
+import org.codehaus.groovy.runtime.IOGroovyMethods;
+import org.codehaus.groovy.runtime.ResourceGroovyMethods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.Certificate;
+import java.util.Objects;
+
+/**
+ * CodeSource wrapper class that allows specific security policies to be associated with a class
+ * compiled from groovy source.
+ *
+ * @author Steve Goetze
+ * @author Guillaume Laforge
+ * @author Merlyn Albery-Speyer
+ */
+public class GroovyCodeSource {
+
+    /**
+     * The codeSource to be given the generated class.  This can be used by policy file
+     * grants to administer security.
+     */
+    private CodeSource codeSource;
+
+    /**
+     * The name given to the generated class
+     */
+    private String name;
+
+    /**
+     * The groovy source to be compiled and turned into a class
+     */
+    private String scriptText;
+
+    /**
+     * The certificates used to sign the items from the codesource
+     */
+    Certificate[] certs;
+
+    private boolean cachable;
+
+    private File file;
+
+    private URL url;
+
+    public GroovyCodeSource(String script, String name, String codeBase) {
+        this.name = name;
+        this.scriptText = script;
+        this.codeSource = createCodeSource(codeBase);
+        this.cachable = true;
+    }
+
+    /**
+     * Construct a GroovyCodeSource for an inputStream of groovyCode that has an
+     * unknown provenance -- meaning it didn't come from a File or a URL (e.g.&#160;a String).
+     * The supplied codeBase will be used to construct a File URL that should match up
+     * with a java Policy entry that determines the grants to be associated with the
+     * class that will be built from the InputStream.
+     * <p>
+     * The permission groovy.security.GroovyCodeSourcePermission will be used to determine if the given codeBase
+     * may be specified.  That is, the current Policy set must have a GroovyCodeSourcePermission that implies
+     * the codeBase, or an exception will be thrown.  This is to prevent callers from hijacking
+     * existing codeBase policy entries unless explicitly authorized by the user.
+     */
+    public GroovyCodeSource(Reader reader, String name, String codeBase) {
+        this.name = name;
+        this.codeSource = createCodeSource(codeBase);
+
+        try {
+            this.scriptText = IOGroovyMethods.getText(reader);
+        } catch (IOException e) {
+            throw new RuntimeException("Impossible to read the text content from that reader, for script: " + name + " with codeBase: " + codeBase, e);
+        }
+    }
+
+    public GroovyCodeSource(final File infile, final String encoding) throws IOException {
+        // avoid files which confuse us like ones with .. in path
+        final File file = new File(infile.getCanonicalPath());
+        if (!file.exists()) {
+            throw new FileNotFoundException(file.toString() + " (" + file.getAbsolutePath() + ")");
+        }
+        if (file.isDirectory()) {
+            throw new IllegalArgumentException(file.toString() + " (" + file.getAbsolutePath() + ") is a directory not a Groovy source file.");
+        }
+        try {
+            if (!file.canRead())
+                throw new RuntimeException(file.toString() + " can not be read. Check the read permission of the file \"" + file.toString() + "\" (" + file.getAbsolutePath() + ").");
+        }
+        catch (SecurityException e) {
+            throw e;
+        }
+
+        this.file = file;
+        this.cachable = true;
+        //The calls below require access to user.dir - allow here since getName() and getCodeSource() are
+        //package private and used only by the GroovyClassLoader.
+        try {
+            Object[] info = AccessController.doPrivileged(new PrivilegedExceptionAction<Object[]>() {
+                public Object[] run() throws IOException {
+                    // retrieve the content of the file using the provided encoding
+                    if (encoding != null) {
+                        scriptText = ResourceGroovyMethods.getText(infile, encoding);
+                    } else {
+                        scriptText = ResourceGroovyMethods.getText(infile);
+                    }
+
+                    Object[] info = new Object[2];
+                    URL url = file.toURI().toURL();
+                    info[0] = url.toExternalForm();
+                    //toURI().toURL() will encode, but toURL() will not.
+                    info[1] = new CodeSource(url, (Certificate[]) null);
+                    return info;
+                }
+            });
+
+            this.name = (String) info[0];
+            this.codeSource = (CodeSource) info[1];
+        } catch (PrivilegedActionException pae) {
+            Throwable cause = pae.getCause();
+            if (cause != null && cause instanceof IOException) {
+                throw (IOException) cause;
+            }
+            throw new RuntimeException("Could not construct CodeSource for file: " + file, cause);
+        }
+    }
+
+    /**
+     * @param infile the file to create a GroovyCodeSource for.
+     * @throws IOException if an issue arises opening and reading the file.
+     */
+    public GroovyCodeSource(final File infile) throws IOException {
+        this(infile, CharsetToolkit.getDefaultSystemCharset().name());
+    }
+
+    public GroovyCodeSource(URI uri) throws IOException {
+        this(uri.toURL());
+    }
+
+    public GroovyCodeSource(URL url) {
+        if (url == null) {
+            throw new RuntimeException("Could not construct a GroovyCodeSource from a null URL");
+        }
+        this.url = url;
+        // TODO: GROOVY-6561: GroovyMain got the name this way: script.substring(script.lastIndexOf("/") + 1)
+        this.name = url.toExternalForm();
+        this.codeSource = new CodeSource(url, (java.security.cert.Certificate[]) null);
+        try {
+            String contentEncoding = getContentEncoding(url);
+            if (contentEncoding != null) {
+                this.scriptText = ResourceGroovyMethods.getText(url, contentEncoding);
+            } else {
+                this.scriptText = ResourceGroovyMethods.getText(url); // falls-back on default encoding
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Impossible to read the text content from " + name, e);
+        }
+    }
+
+    /**
+     * TODO(jwagenleitner): remove or fix in future release
+     *
+     * According to the spec getContentEncoding() returns the Content-Encoding
+     * HTTP Header which typically carries values such as 'gzip' or 'deflate'
+     * and is not the character set encoding. For compatibility in 2.4.x,
+     * this behavior is retained but should be removed or fixed (parse
+     * charset from Content-Type header) in future releases.
+     *
+     * see GROOVY-8056 and https://github.com/apache/groovy/pull/500
+     */
+    private static String getContentEncoding(URL url) throws IOException {
+        URLConnection urlConnection = url.openConnection();
+        String encoding = urlConnection.getContentEncoding();
+        try {
+            IOGroovyMethods.closeQuietly(urlConnection.getInputStream());
+        } catch (IOException ignore) {
+            // For compatibility, ignore exceptions from getInputStream() call
+        }
+        return encoding;
+    }
+
+    CodeSource getCodeSource() {
+        return codeSource;
+    }
+
+    public String getScriptText() {
+        return scriptText;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public URL getURL() {
+        return url;
+    }
+
+    public void setCachable(boolean b) {
+        cachable = b;
+    }
+
+    public boolean isCachable() {
+        return cachable;
+    }
+
+    private static CodeSource createCodeSource(final String codeBase) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new GroovyCodeSourcePermission(codeBase));
+        }
+        try {
+            return new CodeSource(new URL("file", "", codeBase), (java.security.cert.Certificate[]) null);
+        }
+        catch (MalformedURLException e) {
+            throw new RuntimeException("A CodeSource file URL cannot be constructed from the supplied codeBase: " + codeBase);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        GroovyCodeSource that = (GroovyCodeSource) o;
+        return Objects.equals(codeSource, that.codeSource);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(codeSource);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyInterceptable.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyInterceptable.java b/src/main/groovy/groovy/lang/GroovyInterceptable.java
new file mode 100644
index 0000000..8f83354
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyInterceptable.java
@@ -0,0 +1,28 @@
+/*
+ *  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 groovy.lang;
+
+/**
+ * Marker interface used to notify that all methods should be intercepted through the <code>invokeMethod</code> mechanism
+ * of <code>GroovyObject</code>.
+ *
+ * @author Guillaume Laforge
+ */
+public interface GroovyInterceptable extends GroovyObject {
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyObject.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyObject.java b/src/main/groovy/groovy/lang/GroovyObject.java
new file mode 100644
index 0000000..5f271e3
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyObject.java
@@ -0,0 +1,68 @@
+/*
+ *  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 groovy.lang;
+
+/**
+ * The interface implemented by all Groovy objects.
+ * <p>
+ * Especially handy for using Groovy objects when in the Java world.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public interface GroovyObject {
+
+    /**
+     * Invokes the given method.
+     *
+     * @param name the name of the method to call
+     * @param args the arguments to use for the method call
+     * @return the result of invoking the method
+     */
+    Object invokeMethod(String name, Object args);
+
+    /**
+     * Retrieves a property value.
+     *
+     * @param propertyName the name of the property of interest
+     * @return the given property
+     */
+    Object getProperty(String propertyName);
+
+    /**
+     * Sets the given property to the new value.
+     *
+     * @param propertyName the name of the property of interest
+     * @param newValue     the new value for the property
+     */
+    void setProperty(String propertyName, Object newValue);
+
+    /**
+     * Returns the metaclass for a given class.
+     *
+     * @return the metaClass of this instance
+     */
+    MetaClass getMetaClass();
+
+    /**
+     * Allows the MetaClass to be replaced with a derived implementation.
+     *
+     * @param metaClass the new metaclass
+     */
+    void setMetaClass(MetaClass metaClass);
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyObjectSupport.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyObjectSupport.java b/src/main/groovy/groovy/lang/GroovyObjectSupport.java
new file mode 100644
index 0000000..cf7a77f
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyObjectSupport.java
@@ -0,0 +1,63 @@
+/*
+ *  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 groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * A useful base class for Java objects wishing to be Groovy objects
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public abstract class GroovyObjectSupport implements GroovyObject {
+
+    // never persist the MetaClass
+    private transient MetaClass metaClass;
+
+    public GroovyObjectSupport() {
+        this.metaClass = getDefaultMetaClass();
+    }
+
+    public Object getProperty(String property) {
+        return getMetaClass().getProperty(this, property);
+    }
+
+    public void setProperty(String property, Object newValue) {
+        getMetaClass().setProperty(this, property, newValue);
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        return getMetaClass().invokeMethod(this, name, args);
+    }
+
+    public MetaClass getMetaClass() {
+        return this.metaClass;
+    }
+
+    public void setMetaClass(MetaClass metaClass) {
+        this.metaClass =
+                null == metaClass
+                    ? getDefaultMetaClass()
+                    : metaClass;
+    }
+
+    private MetaClass getDefaultMetaClass() {
+        return InvokerHelper.getMetaClass(this.getClass());
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyResourceLoader.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyResourceLoader.java b/src/main/groovy/groovy/lang/GroovyResourceLoader.java
new file mode 100644
index 0000000..7abe202
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyResourceLoader.java
@@ -0,0 +1,39 @@
+/*
+ *  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 groovy.lang;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Allows frameworks that integrate with Groovy to determine how Groovy files are resolved.
+ * 
+ * @author Steven Devijver
+ */
+public interface GroovyResourceLoader {
+
+    /**
+     * Loads a Groovy source file given its name.
+     *
+     * @param filename name of the file
+     * @return a URL
+     * @throws java.net.MalformedURLException if the URL is invalid
+     */
+    URL loadGroovySource(String filename) throws MalformedURLException;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyRuntimeException.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyRuntimeException.java b/src/main/groovy/groovy/lang/GroovyRuntimeException.java
new file mode 100644
index 0000000..33eb51c
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyRuntimeException.java
@@ -0,0 +1,88 @@
+/*
+ *  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 groovy.lang;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ModuleNode;
+
+/**
+ * An exception thrown by the interpreter
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class GroovyRuntimeException extends RuntimeException {
+
+    private ModuleNode module;
+    private ASTNode node;
+
+    public GroovyRuntimeException() {
+    }
+
+    public GroovyRuntimeException(String message) {
+        super(message);
+    }
+
+    public GroovyRuntimeException(String message, ASTNode node) {
+        super(message);
+        this.node = node;
+    }
+
+    public GroovyRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public GroovyRuntimeException(Throwable t) {
+        super();
+        initCause(t);
+    }
+
+    public void setModule(ModuleNode module) {
+        this.module = module;
+    }
+
+    public ModuleNode getModule() {
+        return module;
+    }
+
+    public String getMessage() {
+        return getMessageWithoutLocationText() + getLocationText();
+    }
+
+    public ASTNode getNode() {
+        return node;
+    }
+
+    public String getMessageWithoutLocationText() {
+        return super.getMessage();
+    }
+
+    protected String getLocationText() {
+        String answer = ". ";
+        if (node != null) {
+            answer += "At [" + node.getLineNumber() + ":" + node.getColumnNumber() + "] ";
+        }
+        if (module != null) {
+            answer += module.getDescription();
+        }
+        if (answer.equals(". ")) {
+            return "";
+        }
+        return answer;
+    }
+}