From c4d4e64c04b3802c9212f69ec94631aa70a31a64 Mon Sep 17 00:00:00 2001
From: Uwe Schindler <uwe@thetaphi.de>
Date: Tue, 31 Jan 2017 22:17:33 +0100
Subject: [PATCH] Improvement for #116: Compile script to a class that is
 reused for each invocation instead of an instance (according to Groovy
 guidelines)

---
 .../gradle/ForbiddenApisPlugin.java           | 38 ++++++++++++++-----
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java
index f14be05e..d506ad15 100644
--- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java
+++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java
@@ -17,16 +17,20 @@
 package de.thetaphi.forbiddenapis.gradle;
 
 import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
 import groovy.lang.GroovyCodeSource;
-import groovy.lang.GroovyShell;
 import groovy.util.DelegatingScript;
 
+import java.lang.reflect.Constructor;
 import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
+import org.gradle.api.plugins.PluginInstantiationException;
 
 /**
  * Forbiddenapis Gradle Plugin (requires at least Gradle 2.3)
@@ -43,28 +47,42 @@ public class ForbiddenApisPlugin implements Plugin<Project> {
   /** Name of the extension to define defaults for all tasks of this module. */
   public static final String FORBIDDEN_APIS_EXTENSION_NAME = "forbiddenApis";
   
-  private static final DelegatingScript compiledScript;
+  private static final Constructor<? extends DelegatingScript> compiledScriptCtor;
   static {
     final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName());
     final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer);
     configuration.setScriptBaseClass(DelegatingScript.class.getName());
     configuration.setSourceEncoding("UTF-8");
-    final GroovyShell shell = new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), new Binding(), configuration);
     final URL scriptUrl = ForbiddenApisPlugin.class.getResource(PLUGIN_INIT_SCRIPT);
     if (scriptUrl == null) {
       throw new RuntimeException("Cannot find resource with script: " + PLUGIN_INIT_SCRIPT);
     }
-    final GroovyCodeSource csrc = new GroovyCodeSource(scriptUrl);
-    compiledScript = (DelegatingScript) shell.parse(csrc);
+    compiledScriptCtor = AccessController.doPrivileged(new PrivilegedAction<Constructor<? extends DelegatingScript>>() {
+      @Override
+      public Constructor<? extends DelegatingScript> run() {
+        try {
+          final GroovyClassLoader loader = new GroovyClassLoader(ForbiddenApisPlugin.class.getClassLoader(), configuration);
+          final GroovyCodeSource csrc = new GroovyCodeSource(scriptUrl);
+          @SuppressWarnings("unchecked") final Class<? extends DelegatingScript> clazz =
+              loader.parseClass(csrc, false).asSubclass(DelegatingScript.class);
+          return clazz.getConstructor(Binding.class);
+        } catch (Exception e) {
+          throw new RuntimeException("Cannot compile Groovy script: " + PLUGIN_INIT_SCRIPT);
+        }
+      }
+    });
   }
   
   @Override
   public void apply(Project project) {
-    synchronized(compiledScript) {
-      compiledScript.setDelegate(this);
-      compiledScript.setProperty("project", project);
-      compiledScript.run();
-      compiledScript.setProperty("project", null); // free resources
+    final Binding binding = new Binding();
+    binding.setVariable("project", project);
+    try {
+      final DelegatingScript script = compiledScriptCtor.newInstance(binding);
+      script.setDelegate(this);
+      script.run();
+    } catch (Exception e) {
+      throw new PluginInstantiationException("Cannot execute Groovy script for apply(Project).", e);
     }
   }