Browse Source
We check for existence of the sources and (as before) resolve multiple resources on the classpath if a path is not a File. In addition supports Spring pseudo-URL prefixes as well as normal URLs as source locations. In addition sources can now be specified as a directory (searched recursively by default), or a resource pattern (e.g. app/**/*.groovy). Fixes gh-207pull/208/head
15 changed files with 560 additions and 200 deletions
@ -1,140 +0,0 @@
@@ -1,140 +0,0 @@
|
||||
/* |
||||
* 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 java.io.File; |
||||
import java.io.IOException; |
||||
import java.net.URL; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.Enumeration; |
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
/** |
||||
* Extract file options (anything following '--' in an {@link OptionSet}). |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Greg Turnquist |
||||
*/ |
||||
public class FileOptions { |
||||
|
||||
private List<File> files; |
||||
|
||||
private List<?> args; |
||||
|
||||
/** |
||||
* Create a new {@link FileOptions} instance. |
||||
* @param options the source option set |
||||
*/ |
||||
public FileOptions(OptionSet options) { |
||||
this(options, null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link FileOptions} instance. If it is an error to pass options that |
||||
* specify non-existent files, but the default paths are allowed not to exist (the |
||||
* paths are tested before use). If default paths are provided and the option set |
||||
* contains no file arguments it is not an error even if none of the default paths |
||||
* exist). |
||||
* |
||||
* @param optionSet the source option set |
||||
* @param classLoader an optional classloader used to try and load files that are not |
||||
* found in the local filesystem |
||||
* @param defaultPaths the default paths to use if no files are provided in the option |
||||
* set |
||||
*/ |
||||
public FileOptions(OptionSet optionSet, ClassLoader classLoader, |
||||
String... defaultPaths) { |
||||
List<?> nonOptionArguments = optionSet.nonOptionArguments(); |
||||
List<File> files = new ArrayList<File>(); |
||||
for (Object option : nonOptionArguments) { |
||||
if (option instanceof String) { |
||||
String filename = (String) option; |
||||
if ("--".equals(filename)) { |
||||
break; |
||||
} |
||||
if (filename.endsWith(".groovy") || filename.endsWith(".java")) { |
||||
List<File> file = getFiles(filename, classLoader); |
||||
if (file.isEmpty()) { |
||||
throw new IllegalArgumentException("Can't find " + filename); |
||||
} |
||||
files.addAll(file); |
||||
} |
||||
} |
||||
} |
||||
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) { |
||||
for (File file : getFiles(path, classLoader)) { |
||||
if (file != null && file.exists()) { |
||||
files.add(file); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
this.files = Collections.unmodifiableList(files); |
||||
} |
||||
|
||||
private List<File> getFiles(String filename, ClassLoader classLoader) { |
||||
File file = new File(filename); |
||||
if (file.isFile() && file.canRead()) { |
||||
return Arrays.asList(file); |
||||
} |
||||
List<File> result = new ArrayList<File>(); |
||||
if (classLoader != null) { |
||||
Enumeration<URL> urls; |
||||
try { |
||||
urls = classLoader.getResources(filename); |
||||
while (urls.hasMoreElements()) { |
||||
URL url = urls.nextElement(); |
||||
if (url != null && url.toString().startsWith("file:")) { |
||||
result.add(new File(url.toString().substring("file:".length()))); |
||||
} |
||||
} |
||||
} |
||||
catch (IOException e) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public List<?> getArgs() { |
||||
return this.args; |
||||
} |
||||
|
||||
public String[] getArgsArray() { |
||||
return this.args.toArray(new String[this.args.size()]); |
||||
} |
||||
|
||||
public List<File> getFiles() { |
||||
return this.files; |
||||
} |
||||
|
||||
public File[] getFilesArray() { |
||||
return this.files.toArray(new File[this.files.size()]); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
/* |
||||
* 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 java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
import org.springframework.boot.cli.util.ResourceUtils; |
||||
|
||||
/** |
||||
* Extract source file options (anything following '--' in an {@link OptionSet}). |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Greg Turnquist |
||||
*/ |
||||
public class SourceOptions { |
||||
|
||||
private List<String> sources; |
||||
|
||||
private List<?> args; |
||||
|
||||
/** |
||||
* Create a new {@link SourceOptions} instance. |
||||
* @param options the source option set |
||||
*/ |
||||
public SourceOptions(OptionSet options) { |
||||
this(options, null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link SourceOptions} instance. If it is an error to pass options that |
||||
* specify non-existent sources, but the default paths are allowed not to exist (the |
||||
* paths are tested before use). If default paths are provided and the option set |
||||
* contains no source file arguments it is not an error even if none of the default |
||||
* paths exist). |
||||
* |
||||
* @param optionSet the source option set |
||||
* @param classLoader an optional classloader used to try and load files that are not |
||||
* found in the local filesystem |
||||
* @param defaultPaths the default paths to use if no files are provided in the option |
||||
* set |
||||
*/ |
||||
public SourceOptions(OptionSet optionSet, ClassLoader classLoader, |
||||
String... defaultPaths) { |
||||
List<?> nonOptionArguments = optionSet.nonOptionArguments(); |
||||
List<String> sources = new ArrayList<String>(); |
||||
for (Object option : nonOptionArguments) { |
||||
if (option instanceof String) { |
||||
String filename = (String) option; |
||||
if ("--".equals(filename)) { |
||||
break; |
||||
} |
||||
List<String> urls = ResourceUtils.getUrls(filename, classLoader); |
||||
for (String url : urls) { |
||||
if (url.endsWith(".groovy") || url.endsWith(".java")) { |
||||
sources.add(url); |
||||
} |
||||
} |
||||
if ((filename.endsWith(".groovy") || filename.endsWith(".java")) |
||||
&& urls.isEmpty()) { |
||||
throw new IllegalArgumentException("Can't find " + filename); |
||||
} |
||||
} |
||||
} |
||||
this.args = Collections.unmodifiableList(nonOptionArguments.subList( |
||||
sources.size(), nonOptionArguments.size())); |
||||
if (sources.size() == 0) { |
||||
if (defaultPaths.length == 0) { |
||||
throw new IllegalArgumentException( |
||||
"Please specify at least one file to run"); |
||||
} |
||||
for (String path : defaultPaths) { |
||||
sources.addAll(ResourceUtils.getUrls(path, classLoader)); |
||||
} |
||||
} |
||||
this.sources = Collections.unmodifiableList(sources); |
||||
} |
||||
|
||||
public List<?> getArgs() { |
||||
return this.args; |
||||
} |
||||
|
||||
public String[] getArgsArray() { |
||||
return this.args.toArray(new String[this.args.size()]); |
||||
} |
||||
|
||||
public List<String> getSources() { |
||||
return this.sources; |
||||
} |
||||
|
||||
public String[] getSourcesArray() { |
||||
return this.sources.toArray(new String[this.sources.size()]); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
/* |
||||
* 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.util; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URL; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.Enumeration; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.core.io.FileSystemResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.UrlResource; |
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Utilities for manipulating resource paths and URLs. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public abstract class ResourceUtils { |
||||
|
||||
/** Pseudo URL prefix for loading from the class path: "classpath:" */ |
||||
public static final String CLASSPATH_URL_PREFIX = "classpath:"; |
||||
|
||||
/** Pseudo URL prefix for loading all resources from the class path: "classpath*:" */ |
||||
public static final String ALL_CLASSPATH_URL_PREFIX = "classpath*:"; |
||||
|
||||
/** URL prefix for loading from the file system: "file:" */ |
||||
public static final String FILE_URL_PREFIX = "file:"; |
||||
|
||||
/** Wildcard character in source path */ |
||||
public static final CharSequence WILDCARD = "*"; |
||||
|
||||
public static List<String> getUrls(String path, ClassLoader classLoader) { |
||||
|
||||
if (classLoader == null) { |
||||
classLoader = ClassUtils.getDefaultClassLoader(); |
||||
} |
||||
|
||||
path = StringUtils.cleanPath(path); |
||||
if (path.contains(WILDCARD)) { |
||||
if (path.contains(":")) { |
||||
try { |
||||
Resource[] resources = new PathMatchingResourcePatternResolver( |
||||
classLoader).getResources(path); |
||||
List<String> result = new ArrayList<String>(); |
||||
for (Resource resource : resources) { |
||||
result.add(resource.getURL().toExternalForm()); |
||||
} |
||||
return result; |
||||
} |
||||
catch (IOException e) { |
||||
throw new IllegalArgumentException("Cannot resolve paths at [" + path |
||||
+ "]", e); |
||||
} |
||||
} |
||||
else { |
||||
try { |
||||
return getUrls(FILE_URL_PREFIX + path, classLoader); |
||||
} |
||||
catch (IllegalArgumentException e) { |
||||
// ignore
|
||||
} |
||||
return getUrls(ALL_CLASSPATH_URL_PREFIX + path, classLoader); |
||||
} |
||||
} |
||||
|
||||
if (path.contains(":")) { |
||||
|
||||
if (path.startsWith(CLASSPATH_URL_PREFIX)) { |
||||
path = path.substring(CLASSPATH_URL_PREFIX.length()); |
||||
} |
||||
else { |
||||
return getFilePath(path); |
||||
} |
||||
|
||||
} |
||||
else { |
||||
try { |
||||
return getFilePath(path); |
||||
} |
||||
catch (IllegalArgumentException e) { |
||||
// ignore
|
||||
} |
||||
} |
||||
|
||||
while (path.startsWith("/")) { |
||||
path = path.substring(1); |
||||
} |
||||
List<String> result = new ArrayList<String>(); |
||||
if (classLoader != null) { |
||||
Enumeration<URL> urls; |
||||
try { |
||||
urls = classLoader.getResources(path); |
||||
while (urls.hasMoreElements()) { |
||||
URL url = urls.nextElement(); |
||||
result.add(url.toExternalForm()); |
||||
} |
||||
} |
||||
catch (IOException e) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static List<String> getFilePath(String path) { |
||||
FileSystemResource resource = new FileSystemResource(path); |
||||
if (resource.exists()) { |
||||
try { |
||||
if (resource.getFile().isDirectory()) { |
||||
Resource[] resources = new PathMatchingResourcePatternResolver() |
||||
.getResources(resource.getURL() + "/**"); |
||||
List<String> result = new ArrayList<String>(); |
||||
for (Resource sub : resources) { |
||||
if (!sub.getFile().isDirectory()) { |
||||
result.add(sub.getURL().toExternalForm()); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
return Collections.singletonList(resource.getURL().toExternalForm()); |
||||
} |
||||
catch (IOException e) { |
||||
throw new IllegalArgumentException("Cannot create URL from path [" + path |
||||
+ "]", e); |
||||
} |
||||
} |
||||
try { |
||||
UrlResource url = new UrlResource(path); |
||||
if (url.exists()) { |
||||
try { |
||||
return Collections.singletonList(url.getURL().toExternalForm()); |
||||
} |
||||
catch (IOException e) { |
||||
throw new IllegalArgumentException("Cannot create URL from path [" |
||||
+ path + "]", e); |
||||
} |
||||
} |
||||
} |
||||
catch (MalformedURLException ex) { |
||||
throw new IllegalArgumentException("Cannot create URL from path [" + path |
||||
+ "]", ex); |
||||
} |
||||
return Collections.emptyList(); |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* 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 for code in directories. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class DirectorySourcesIntegrationTests { |
||||
|
||||
@Rule |
||||
public CliTester cli = new CliTester("src/test/resources/dir-sample/"); |
||||
|
||||
@Test |
||||
public void runDirectory() throws Exception { |
||||
this.cli.run("code"); |
||||
assertThat(this.cli.getOutput(), containsString("Hello World")); |
||||
} |
||||
|
||||
@Test |
||||
public void runDirectoryRecursive() throws Exception { |
||||
this.cli.run(""); |
||||
assertThat(this.cli.getOutput(), containsString("Hello World")); |
||||
} |
||||
|
||||
@Test |
||||
public void runPathPattern() throws Exception { |
||||
this.cli.run("**/*.groovy"); |
||||
assertThat(this.cli.getOutput(), containsString("Hello World")); |
||||
} |
||||
} |
||||
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
/* |
||||
* 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.util; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
/** |
||||
* @author Dave Syer |
||||
*/ |
||||
public class ResourceUtilsTests { |
||||
|
||||
@Test |
||||
public void explicitClasspathResource() { |
||||
List<String> urls = ResourceUtils.getUrls("classpath:init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void explicitClasspathResourceWithSlash() { |
||||
List<String> urls = ResourceUtils.getUrls("classpath:/init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void implicitClasspathResource() { |
||||
List<String> urls = ResourceUtils.getUrls("init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void implicitClasspathResourceWithSlash() { |
||||
List<String> urls = ResourceUtils.getUrls("/init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void nonexistentClasspathResource() { |
||||
List<String> urls = ResourceUtils.getUrls("classpath:nonexistent.groovy", null); |
||||
assertEquals(0, urls.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void explicitFile() { |
||||
List<String> urls = ResourceUtils.getUrls("file:src/test/resources/init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void implicitFile() { |
||||
List<String> urls = ResourceUtils.getUrls("src/test/resources/init.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void nonexistentFile() { |
||||
List<String> urls = ResourceUtils.getUrls("file:nonexistent.groovy", null); |
||||
assertEquals(0, urls.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void recursiveFiles() { |
||||
List<String> urls = ResourceUtils.getUrls("src/test/resources/dir-sample", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void recursiveFilesByPatternWithPrefix() { |
||||
List<String> urls = ResourceUtils.getUrls( |
||||
"file:src/test/resources/dir-sample/**/*.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void recursiveFilesByPattern() { |
||||
List<String> urls = ResourceUtils.getUrls( |
||||
"src/test/resources/dir-sample/**/*.groovy", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void directoryOfFilesWithPrefix() { |
||||
List<String> urls = ResourceUtils.getUrls( |
||||
"file:src/test/resources/dir-sample/code/*", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
@Test |
||||
public void directoryOfFiles() { |
||||
List<String> urls = ResourceUtils.getUrls("src/test/resources/dir-sample/code/*", |
||||
ClassUtils.getDefaultClassLoader()); |
||||
assertEquals(1, urls.size()); |
||||
assertTrue(urls.get(0).startsWith("file:")); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package org.test |
||||
|
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
void run(String... args) { |
||||
println "Hello ${this.myService.sayWorld()} From ${getClass().getClassLoader().getResource('samples/app.groovy')}" |
||||
} |
||||
} |
||||
|
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
String sayWorld() { |
||||
return "World!" |
||||
} |
||||
} |
||||
|
||||
|
||||
Loading…
Reference in new issue