();
for (Object option : nonOptionArguments) {
@@ -63,18 +71,26 @@ public class FileOptions {
if (filename.endsWith(".groovy") || filename.endsWith(".java")) {
File file = getFile(filename, classLoader);
if (file == null) {
- throw new RuntimeException("Can't find " + filename);
+ throw new IllegalArgumentException("Can't find " + filename);
}
files.add(file);
}
}
}
- if (files.size() == 0) {
- throw new RuntimeException("Please specify a file to run");
- }
- this.files = Collections.unmodifiableList(files);
this.args = Collections.unmodifiableList(nonOptionArguments.subList(files.size(),
nonOptionArguments.size()));
+ if (files.size() == 0) {
+ if (defaultPaths.length == 0) {
+ throw new RuntimeException("Please specify at least one file to run");
+ }
+ for (String path : defaultPaths) {
+ File file = getFile(path, classLoader);
+ if (file != null && file.exists()) {
+ files.add(file);
+ }
+ }
+ }
+ this.files = Collections.unmodifiableList(files);
}
private File getFile(String filename, ClassLoader classLoader) {
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java
new file mode 100644
index 00000000000..272f7f48520
--- /dev/null
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java
@@ -0,0 +1,141 @@
+/*
+ * 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.command;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.Script;
+
+import java.io.File;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import joptsimple.OptionSet;
+
+import org.springframework.boot.cli.Command;
+import org.springframework.boot.cli.CommandFactory;
+import org.springframework.boot.cli.SpringCli;
+import org.springframework.boot.cli.compiler.GroovyCompiler;
+import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
+import org.springframework.boot.cli.compiler.GroovyCompilerConfigurationAdapter;
+import org.springframework.boot.cli.compiler.GroovyCompilerScope;
+import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
+import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
+
+/**
+ *
+ * Command to initialize the Spring CLI with commands from the classpath. If the current
+ * context class loader is a GroovyClassLoader then it can be enhanced by passing in
+ * compiler options (e.g. --classpath=...).
+ *
+ *
+ * If the current context class loader is not already GroovyClassLoader then one will be
+ * created and will replace the current context loader. In this case command arguments can
+ * include files to compile that have @Grab annotations to process. By
+ * default a script called "init.groovy" or "spring.groovy" is used if it exists in the
+ * current directory or the root of the classpath.
+ *
+ *
+ * @author Dave Syer
+ */
+public class InitCommand extends OptionParsingCommand {
+
+ public static final String NAME = "init";
+
+ public InitCommand(SpringCli cli) {
+ super(NAME, "(Re)-initialize the Spring cli", new InitOptionHandler(cli));
+ }
+
+ private static class InitOptionHandler extends CompilerOptionHandler {
+
+ private SpringCli cli;
+ private GroovyCompiler compiler;
+
+ public InitOptionHandler(SpringCli cli) {
+ this.cli = cli;
+ }
+
+ @Override
+ protected void run(OptionSet options) throws Exception {
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ boolean enhanced = false;
+
+ FileOptions fileOptions = new FileOptions(options, loader, "init.groovy",
+ "spring.groovy");
+ File[] files = fileOptions.getFilesArray();
+
+ if (!(loader instanceof GroovyClassLoader)) {
+
+ List repositoryConfiguration = RepositoryConfigurationFactory
+ .createDefaultRepositoryConfiguration();
+
+ GroovyCompilerConfiguration configuration = new InitGroovyCompilerConfigurationAdapter(
+ options, this, repositoryConfiguration);
+
+ this.compiler = new GroovyCompiler(configuration);
+ loader = this.compiler.getLoader();
+ Thread.currentThread().setContextClassLoader(loader);
+
+ }
+ else {
+ String classpath = getClasspathOption().value(options);
+ if (classpath != null && classpath.length() > 0) {
+ ((GroovyClassLoader) loader).addClasspath(classpath);
+ enhanced = true;
+ }
+ }
+
+ if (this.compiler != null && files.length > 0) {
+ Class>[] classes = this.compiler.compile(files);
+ for (Class> type : classes) {
+ if (Script.class.isAssignableFrom(type)) {
+ ((Script) type.newInstance()).run();
+ }
+ }
+ enhanced = true;
+ }
+
+ if (this.cli.getCommands().isEmpty() || enhanced) {
+
+ for (CommandFactory factory : ServiceLoader.load(CommandFactory.class,
+ loader)) {
+ for (Command command : factory.getCommands(this.cli)) {
+ this.cli.register(command);
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ private static class InitGroovyCompilerConfigurationAdapter extends
+ GroovyCompilerConfigurationAdapter {
+ private InitGroovyCompilerConfigurationAdapter(OptionSet optionSet,
+ CompilerOptionHandler compilerOptionHandler,
+ List repositoryConfiguration) {
+ super(optionSet, compilerOptionHandler, repositoryConfiguration);
+ }
+
+ @Override
+ public GroovyCompilerScope getScope() {
+ return GroovyCompilerScope.EXTENSION;
+ }
+ }
+
+}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java
index f567b74a359..31d67e38ae8 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java
@@ -99,6 +99,7 @@ public class ShellCommand extends AbstractCommand {
PromptCommand prompt = new PromptCommand(this);
cli.register(prompt);
+ cli.register(new InitCommand(cli));
}
private ConsoleReader createConsoleReader() throws IOException {
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 11bf532d150..629196b1b05 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
@@ -46,7 +46,7 @@ import org.springframework.util.FileCopyUtils;
* @author Phillip Webb
* @author Dave Syer
*/
-class ExtendedGroovyClassLoader extends GroovyClassLoader {
+public class ExtendedGroovyClassLoader extends GroovyClassLoader {
private static final String SHARED_PACKAGE = "org.springframework.boot.groovy";
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java
index c8880af12c1..086f7df51b4 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java
@@ -48,7 +48,6 @@ import org.springframework.boot.cli.compiler.grape.AetherGrapeEngine;
import org.springframework.boot.cli.compiler.grape.GrapeEngineInstaller;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
import org.springframework.boot.cli.compiler.transformation.DependencyAutoConfigurationTransformation;
-import org.springframework.boot.cli.compiler.transformation.GrabResolversAutoConfigurationTransformation;
import org.springframework.boot.cli.compiler.transformation.GroovyBeansTransformation;
import org.springframework.boot.cli.compiler.transformation.ResolveDependencyCoordinatesTransformation;
@@ -108,7 +107,7 @@ public class GroovyCompiler {
}
this.transformations = new ArrayList();
- this.transformations.add(new GrabResolversAutoConfigurationTransformation());
+ // this.transformations.add(new GrabResolversAutoConfigurationTransformation());
this.transformations.add(new DependencyAutoConfigurationTransformation(
this.loader, this.coordinatesResolver, this.compilerAutoConfigurations));
this.transformations.add(new GroovyBeansTransformation());
@@ -118,6 +117,10 @@ public class GroovyCompiler {
}
}
+ public ExtendedGroovyClassLoader getLoader() {
+ return this.loader;
+ }
+
private ExtendedGroovyClassLoader createLoader(
GroovyCompilerConfiguration configuration) {
ExtendedGroovyClassLoader loader = new ExtendedGroovyClassLoader(
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompilerScope.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompilerScope.java
index 6693313295f..e5784fc3124 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompilerScope.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompilerScope.java
@@ -24,7 +24,7 @@ package org.springframework.boot.cli.compiler;
public enum GroovyCompilerScope {
/**
- * Default scope, exposes groovy-all.jar (loaded from the parent) and the shared cli
+ * Default scope, exposes groovy.jar (loaded from the parent) and the shared cli
* package (loaded via groovy classloader).
*/
DEFAULT,
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
index 413cfd2f1c6..e015ee1a72b 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
@@ -50,8 +50,8 @@ public final class RepositoryConfigurationFactory {
repositoryConfiguration.add(MAVEN_CENTRAL);
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) {
- repositoryConfiguration.add(SPRING_SNAPSHOT);
repositoryConfiguration.add(SPRING_MILESTONE);
+ repositoryConfiguration.add(SPRING_SNAPSHOT);
}
addDefaultCacheAsRespository(repositoryConfiguration);
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
index 96441a24590..fe24bc5e98e 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
@@ -263,6 +263,9 @@ public class AetherGrapeEngine implements GrapeEngine {
}
protected void addRepository(RemoteRepository repository) {
+ if (this.repositories.contains(repository)) {
+ return;
+ }
if (repository.getProxy() == null) {
RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
builder.setProxy(this.proxySelector.getProxy(repository));
diff --git a/spring-boot-cli/src/test/java/cli/command/CustomCommand.java b/spring-boot-cli/src/test/java/cli/command/CustomCommand.java
new file mode 100644
index 00000000000..24c9a363825
--- /dev/null
+++ b/spring-boot-cli/src/test/java/cli/command/CustomCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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 cli.command;
+
+import org.springframework.boot.cli.command.AbstractCommand;
+
+/**
+ * @author Dave Syer
+ */
+public class CustomCommand extends AbstractCommand {
+
+ public CustomCommand() {
+ super("custom", "Custom command added in tests");
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ System.err.println("Custom Command Hello");
+ }
+
+}
diff --git a/spring-boot-cli/src/test/java/cli/command/CustomCommandFactory.java b/spring-boot-cli/src/test/java/cli/command/CustomCommandFactory.java
new file mode 100644
index 00000000000..0c0231a97a0
--- /dev/null
+++ b/spring-boot-cli/src/test/java/cli/command/CustomCommandFactory.java
@@ -0,0 +1,36 @@
+/*
+ * 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 cli.command;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.springframework.boot.cli.Command;
+import org.springframework.boot.cli.CommandFactory;
+import org.springframework.boot.cli.SpringCli;
+
+/**
+ * @author Dave Syer
+ */
+public class CustomCommandFactory implements CommandFactory {
+
+ @Override
+ public Collection getCommands(SpringCli cli) {
+ return Collections. singleton(new CustomCommand());
+ }
+
+}
diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java
new file mode 100644
index 00000000000..5587618e009
--- /dev/null
+++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java
@@ -0,0 +1,84 @@
+/*
+ * 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.command;
+
+import groovy.lang.GroovyClassLoader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.boot.OutputCapture;
+import org.springframework.boot.cli.Command;
+import org.springframework.boot.cli.SpringCli;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * @author Dave Syer
+ */
+public class InitCommandTests {
+
+ @Rule
+ public OutputCapture output = new OutputCapture();
+
+ private SpringCli cli = mock(SpringCli.class);
+ private InitCommand command = new InitCommand(this.cli);
+ private int defaultCount = new DefaultCommandFactory().getCommands(this.cli).size();
+ private ClassLoader classLoader;
+
+ @Before
+ public void init() {
+ this.classLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ @After
+ public void close() {
+ Thread.currentThread().setContextClassLoader(this.classLoader);
+ }
+
+ @Test
+ public void explicitClasspath() throws Exception {
+ Thread.currentThread().setContextClassLoader(new GroovyClassLoader());
+ this.command.run("--cp=src/test/plugins/custom/custom/0.0.1/custom-0.0.1.jar");
+ verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class));
+ }
+
+ @Test
+ public void initScript() throws Exception {
+ this.command.run("src/test/resources/grab.groovy");
+ verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class));
+ assertTrue(this.output.toString().contains("Hello Grab"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void initNonExistentScript() throws Exception {
+ this.command.run("nonexistent.groovy");
+ }
+
+ // There is an init.groovy on the test classpath so this succeeds
+ @Test
+ public void initDefault() throws Exception {
+ this.command.run();
+ assertTrue(this.output.toString().contains("Hello World"));
+ }
+
+}
diff --git a/spring-boot-cli/src/test/plugins/custom/META-INF/services/org.springframework.boot.cli.CommandFactory b/spring-boot-cli/src/test/plugins/custom/META-INF/services/org.springframework.boot.cli.CommandFactory
new file mode 100644
index 00000000000..727204a63ad
--- /dev/null
+++ b/spring-boot-cli/src/test/plugins/custom/META-INF/services/org.springframework.boot.cli.CommandFactory
@@ -0,0 +1 @@
+cli.command.CustomCommandFactory
diff --git a/spring-boot-cli/src/test/plugins/custom/custom/0.0.1/custom-0.0.1.jar b/spring-boot-cli/src/test/plugins/custom/custom/0.0.1/custom-0.0.1.jar
new file mode 100644
index 00000000000..6623163c931
Binary files /dev/null and b/spring-boot-cli/src/test/plugins/custom/custom/0.0.1/custom-0.0.1.jar differ
diff --git a/spring-boot-cli/src/test/plugins/custom/custom/0.0.1/custom-0.0.1.pom b/spring-boot-cli/src/test/plugins/custom/custom/0.0.1/custom-0.0.1.pom
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/spring-boot-cli/src/test/resources/grab.groovy b/spring-boot-cli/src/test/resources/grab.groovy
new file mode 100644
index 00000000000..d9394424ca7
--- /dev/null
+++ b/spring-boot-cli/src/test/resources/grab.groovy
@@ -0,0 +1,6 @@
+@GrabResolver(name="test", root="file:./src/test/plugins")
+@Grab("custom:custom:0.0.1")
+@Controller
+class Foo {}
+
+println "Hello Grab"
\ No newline at end of file
diff --git a/spring-boot-cli/src/test/resources/init.groovy b/spring-boot-cli/src/test/resources/init.groovy
new file mode 100644
index 00000000000..31665bffc0d
--- /dev/null
+++ b/spring-boot-cli/src/test/resources/init.groovy
@@ -0,0 +1 @@
+println "Hello World"
\ No newline at end of file