diff --git a/spring-boot-cli/pom.xml b/spring-boot-cli/pom.xml index e742bf6686..16056369c8 100644 --- a/spring-boot-cli/pom.xml +++ b/spring-boot-cli/pom.xml @@ -43,7 +43,7 @@ org.codehaus.groovy - groovy-all + groovy org.springframework @@ -94,6 +94,11 @@ aether-util + + org.codehaus.groovy + groovy-templates + provided + junit junit diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ExtendedGroovyClassLoader.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ExtendedGroovyClassLoader.java index 58f8227879..11bf532d15 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ExtendedGroovyClassLoader.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ExtendedGroovyClassLoader.java @@ -26,8 +26,11 @@ import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.control.CompilationUnit; @@ -159,39 +162,39 @@ class ExtendedGroovyClassLoader extends GroovyClassLoader { */ private static class DefaultScopeParentClassLoader extends ClassLoader { + private static final String[] GROOVY_JARS_PREFIXES = { "groovy", "antlr", "asm" }; + private final URLClassLoader groovyOnlyClassLoader; public DefaultScopeParentClassLoader(ClassLoader parent) { super(parent); - this.groovyOnlyClassLoader = new URLClassLoader( - new URL[] { getGroovyJar(parent) }, null); + this.groovyOnlyClassLoader = new URLClassLoader(getGroovyJars(parent), null); } - private URL getGroovyJar(final ClassLoader parent) { - URL result = findGroovyJarDirectly(parent); - if (result == null) { - result = findGroovyJarFromClassPath(parent); + private URL[] getGroovyJars(final ClassLoader parent) { + Set urls = new HashSet(); + findGroovyJarsDirectly(parent, urls); + if (urls.isEmpty()) { + findGroovyJarsFromClassPath(parent, urls); } - Assert.state(result != null, "Unable to find groovy JAR"); - return result; + Assert.state(urls.size() > 0, "Unable to find groovy JAR"); + return new ArrayList(urls).toArray(new URL[urls.size()]); } - private URL findGroovyJarDirectly(ClassLoader classLoader) { + private void findGroovyJarsDirectly(ClassLoader classLoader, Set urls) { while (classLoader != null) { if (classLoader instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) classLoader).getURLs(); - for (URL url : urls) { + for (URL url : ((URLClassLoader) classLoader).getURLs()) { if (isGroovyJar(url.toString())) { - return url; + urls.add(url); } } } classLoader = classLoader.getParent(); } - return null; } - private URL findGroovyJarFromClassPath(ClassLoader parent) { + private void findGroovyJarsFromClassPath(ClassLoader parent, Set urls) { String classpath = System.getProperty("java.class.path"); String[] entries = classpath.split(System.getProperty("path.separator")); for (String entry : entries) { @@ -199,7 +202,7 @@ class ExtendedGroovyClassLoader extends GroovyClassLoader { File file = new File(entry); if (file.canRead()) { try { - return file.toURI().toURL(); + urls.add(file.toURI().toURL()); } catch (MalformedURLException ex) { // Swallow and continue @@ -207,11 +210,15 @@ class ExtendedGroovyClassLoader extends GroovyClassLoader { } } } - return null; } private boolean isGroovyJar(String entry) { - return entry.contains("/groovy-all"); + for (String jarPrefix : GROOVY_JARS_PREFIXES) { + if (entry.contains("/" + jarPrefix + "-")) { + return true; + } + } + return false; } @Override diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java index 66559f468f..17f8dc0758 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java @@ -16,6 +16,10 @@ package org.springframework.boot.cli; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -86,6 +90,26 @@ public class CliTester implements TestRule { return this.outputCapture.apply(new RunLauncherStatement(base), description); } + public String getHttpOutput() { + return getHttpOutput("http://localhost:8080"); + } + + public String getHttpOutput(String uri) { + try { + InputStream stream = URI.create(uri).toURL().openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + String line; + StringBuilder result = new StringBuilder(); + while ((line = reader.readLine()) != null) { + result.append(line); + } + return result.toString(); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + private final class RunLauncherStatement extends Statement { private final Statement base; diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java new file mode 100644 index 0000000000..67270823c3 --- /dev/null +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * 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 org.springframework.boot.cli; + +import org.junit.Rule; +import org.junit.Test; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/** + * Integration tests to exercise reproduce raised issues. + * + * @author Phillip Webb + */ +public class ReproIntegrationTests { + + private static final String SRC = "src/test/resources/repro-samples"; + + @Rule + public CliTester cli = new CliTester(); + + @Test + public void grabAntBuilder() throws Exception { + this.cli.run(SRC + "/grab-ant-builder.groovy"); + assertThat(this.cli.getHttpOutput(), + containsString("{\"message\":\"Hello World\"}")); + } + +} diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java index 0d3f173131..d8478aca2e 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java @@ -16,11 +16,7 @@ package org.springframework.boot.cli; -import java.io.BufferedReader; import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; import org.codehaus.plexus.util.FileUtils; import org.junit.BeforeClass; @@ -85,31 +81,29 @@ public class SampleIntegrationTests { "foo=bar"); assertTrue("Wrong output: " + output, output.contains("completed with the following parameters")); - String result = readEntirely("http://localhost:8080"); + String result = this.cli.getHttpOutput(); assertEquals("World!", result); } @Test public void webSample() throws Exception { this.cli.run("samples/web.groovy"); - String result = readEntirely("http://localhost:8080"); - assertEquals("World!", result); + assertEquals("World!", this.cli.getHttpOutput()); } @Test public void uiSample() throws Exception { this.cli.run("samples/ui.groovy", "--classpath=.:src/test/resources"); - String result = readEntirely("http://localhost:8080"); + String result = this.cli.getHttpOutput(); assertTrue("Wrong output: " + result, result.contains("Hello World")); - result = readEntirely("http://localhost:8080/css/bootstrap.min.css"); + result = this.cli.getHttpOutput("http://localhost:8080/css/bootstrap.min.css"); assertTrue("Wrong output: " + result, result.contains("container")); } @Test public void actuatorSample() throws Exception { this.cli.run("samples/actuator.groovy"); - String result = readEntirely("http://localhost:8080"); - assertEquals("{\"message\":\"Hello World!\"}", result); + assertEquals("{\"message\":\"Hello World!\"}", this.cli.getHttpOutput()); } @Test @@ -156,24 +150,7 @@ public class SampleIntegrationTests { @Test public void deviceSample() throws Exception { this.cli.run("samples/device.groovy"); - String result = readEntirely("http://localhost:8080"); - assertEquals("Hello Normal Device!", result); - } - - private static String readEntirely(String uri) { - try { - InputStream stream = URI.create(uri).toURL().openStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - String line; - StringBuilder result = new StringBuilder(); - while ((line = reader.readLine()) != null) { - result.append(line); - } - return result.toString(); - } - catch (Exception ex) { - throw new IllegalStateException(ex); - } + assertEquals("Hello Normal Device!", this.cli.getHttpOutput()); } } diff --git a/spring-boot-cli/src/test/resources/repro-samples/grab-ant-builder.groovy b/spring-boot-cli/src/test/resources/repro-samples/grab-ant-builder.groovy new file mode 100644 index 0000000000..d25fa42c4b --- /dev/null +++ b/spring-boot-cli/src/test/resources/repro-samples/grab-ant-builder.groovy @@ -0,0 +1,11 @@ +@Grab("org.codehaus.groovy:groovy-ant:2.1.6") + +@RestController +class MainController { + + @RequestMapping("/") + def home() { + new AntBuilder().echo(message:"Hello world") + [message: "Hello World"] + } +}