Browse Source
Rework classloading for launched applications so that CLI classes and dependencies are not visible. This change allows many of the previous hacks and workarounds to be removed. With the exception of the 'org.springframework.boot.groovy' package and 'groovy-all' all user required depndencies are now pulled in via @Grab annotations. The updated classloading algorithm has enabled the following changes: - AetherGrapeEngine is now back in the cli project and the spring-boot-cli-grape project has been removed. The AetherGrapeEngine has also been simplified. - The TestCommand now launches a TestRunner (similar in design to the SpringApplicationRunner) and report test failures directly using the junit TextListener. Adding custom 'testers' source to the users project is no longer required. The previous 'double compile' for tests has also been removed. - Utility classes have been removed in favor of using versions from spring-core. - The CLI jar is now packaged using the 'boot-loader' rather than using the maven shade plugin. This commit also applied minor polish refactoring to a number of classes.pull/118/merge
62 changed files with 1471 additions and 1671 deletions
@ -1,176 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|
||||||
<modelVersion>4.0.0</modelVersion> |
|
||||||
<parent> |
|
||||||
<groupId>org.springframework.boot</groupId> |
|
||||||
<artifactId>spring-boot-parent</artifactId> |
|
||||||
<version>0.5.0.BUILD-SNAPSHOT</version> |
|
||||||
<relativePath>../spring-boot-parent</relativePath> |
|
||||||
</parent> |
|
||||||
<artifactId>spring-boot-cli-grape</artifactId> |
|
||||||
<packaging>jar</packaging> |
|
||||||
<properties> |
|
||||||
<main.basedir>${basedir}/..</main.basedir> |
|
||||||
<start-class>org.springframework.boot.cli.SpringCli</start-class> |
|
||||||
</properties> |
|
||||||
<dependencies> |
|
||||||
<!-- Provided --> |
|
||||||
<dependency> |
|
||||||
<groupId>org.codehaus.groovy</groupId> |
|
||||||
<artifactId>groovy</artifactId> |
|
||||||
<scope>provided</scope> |
|
||||||
</dependency> |
|
||||||
<!-- Compile --> |
|
||||||
<dependency> |
|
||||||
<groupId>commons-logging</groupId> |
|
||||||
<artifactId>commons-logging</artifactId> |
|
||||||
<version>1.1.3</version> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.apache.maven</groupId> |
|
||||||
<artifactId>maven-aether-provider</artifactId> |
|
||||||
<exclusions> |
|
||||||
<exclusion> |
|
||||||
<artifactId>org.eclipse.sisu.plexus</artifactId> |
|
||||||
<groupId>org.eclipse.sisu</groupId> |
|
||||||
</exclusion> |
|
||||||
</exclusions> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-api</artifactId> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-connector-basic</artifactId> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-impl</artifactId> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-spi</artifactId> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-transport-file</artifactId> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-transport-http</artifactId> |
|
||||||
<exclusions> |
|
||||||
<exclusion> |
|
||||||
<artifactId>jcl-over-slf4j</artifactId> |
|
||||||
<groupId>org.slf4j</groupId> |
|
||||||
</exclusion> |
|
||||||
</exclusions> |
|
||||||
</dependency> |
|
||||||
<dependency> |
|
||||||
<groupId>org.eclipse.aether</groupId> |
|
||||||
<artifactId>aether-util</artifactId> |
|
||||||
</dependency> |
|
||||||
<!-- Test --> |
|
||||||
<dependency> |
|
||||||
<groupId>junit</groupId> |
|
||||||
<artifactId>junit</artifactId> |
|
||||||
<scope>test</scope> |
|
||||||
</dependency> |
|
||||||
</dependencies> |
|
||||||
<build> |
|
||||||
<plugins> |
|
||||||
<plugin> |
|
||||||
<artifactId>maven-surefire-plugin</artifactId> |
|
||||||
<configuration> |
|
||||||
<classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-core</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-beans</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-aop</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-tx</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-expression</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-context</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework:spring-test</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework.retry:spring-retry</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework.integration:spring-integration-core</classpathDependencyExcludes> |
|
||||||
<classpathDependencyExcludes>org.springframework.integration:spring-integration-dsl-groovy-core</classpathDependencyExcludes> |
|
||||||
</classpathDependencyExcludes> |
|
||||||
</configuration> |
|
||||||
</plugin> |
|
||||||
<plugin> |
|
||||||
<artifactId>maven-shade-plugin</artifactId> |
|
||||||
<configuration> |
|
||||||
<filters> |
|
||||||
<filter> |
|
||||||
<artifact>*:*</artifact> |
|
||||||
<excludes> |
|
||||||
<exclude>META-INF/*.SF</exclude> |
|
||||||
<exclude>META-INF/*.DSA</exclude> |
|
||||||
<exclude>META-INF/*.RSA</exclude> |
|
||||||
</excludes> |
|
||||||
</filter> |
|
||||||
</filters> |
|
||||||
</configuration> |
|
||||||
<executions> |
|
||||||
<execution> |
|
||||||
<phase>package</phase> |
|
||||||
<goals> |
|
||||||
<goal>shade</goal> |
|
||||||
</goals> |
|
||||||
<configuration> |
|
||||||
<transformers> |
|
||||||
<transformer |
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> |
|
||||||
<transformer |
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |
|
||||||
<mainClass>${start-class}</mainClass> |
|
||||||
</transformer> |
|
||||||
</transformers> |
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom> |
|
||||||
</configuration> |
|
||||||
</execution> |
|
||||||
</executions> |
|
||||||
</plugin> |
|
||||||
</plugins> |
|
||||||
<pluginManagement> |
|
||||||
<plugins> |
|
||||||
<!--This plugin's configuration is used to store Eclipse m2e settings |
|
||||||
only. It has no influence on the Maven build itself. --> |
|
||||||
<plugin> |
|
||||||
<groupId>org.eclipse.m2e</groupId> |
|
||||||
<artifactId>lifecycle-mapping</artifactId> |
|
||||||
<version>1.0.0</version> |
|
||||||
<configuration> |
|
||||||
<lifecycleMappingMetadata> |
|
||||||
<pluginExecutions> |
|
||||||
<pluginExecution> |
|
||||||
<pluginExecutionFilter> |
|
||||||
<groupId>org.apache.maven.plugins</groupId> |
|
||||||
<artifactId>maven-antrun-plugin</artifactId> |
|
||||||
<versionRange>[1.7,)</versionRange> |
|
||||||
<goals> |
|
||||||
<goal>run</goal> |
|
||||||
</goals> |
|
||||||
</pluginExecutionFilter> |
|
||||||
<action> |
|
||||||
<execute /> |
|
||||||
</action> |
|
||||||
</pluginExecution> |
|
||||||
</pluginExecutions> |
|
||||||
</lifecycleMappingMetadata> |
|
||||||
</configuration> |
|
||||||
</plugin> |
|
||||||
</plugins> |
|
||||||
</pluginManagement> |
|
||||||
</build> |
|
||||||
<pluginRepositories> |
|
||||||
<pluginRepository> |
|
||||||
<id>objectstyle</id> |
|
||||||
<name>ObjectStyle.org Repository</name> |
|
||||||
<url>http://objectstyle.org/maven2/</url> |
|
||||||
<snapshots> |
|
||||||
<enabled>false</enabled> |
|
||||||
</snapshots> |
|
||||||
</pluginRepository> |
|
||||||
</pluginRepositories> |
|
||||||
</project> |
|
||||||
@ -1,355 +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.compiler; |
|
||||||
|
|
||||||
import groovy.grape.GrapeEngine; |
|
||||||
import groovy.lang.GroovyClassLoader; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.net.MalformedURLException; |
|
||||||
import java.net.URI; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Arrays; |
|
||||||
import java.util.Collection; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.TimeUnit; |
|
||||||
|
|
||||||
import org.apache.maven.repository.internal.MavenRepositorySystemUtils; |
|
||||||
import org.eclipse.aether.AbstractRepositoryListener; |
|
||||||
import org.eclipse.aether.DefaultRepositorySystemSession; |
|
||||||
import org.eclipse.aether.RepositoryEvent; |
|
||||||
import org.eclipse.aether.RepositorySystem; |
|
||||||
import org.eclipse.aether.RepositorySystemSession; |
|
||||||
import org.eclipse.aether.artifact.Artifact; |
|
||||||
import org.eclipse.aether.artifact.DefaultArtifact; |
|
||||||
import org.eclipse.aether.collection.CollectRequest; |
|
||||||
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; |
|
||||||
import org.eclipse.aether.graph.Dependency; |
|
||||||
import org.eclipse.aether.graph.Exclusion; |
|
||||||
import org.eclipse.aether.impl.ArtifactDescriptorReader; |
|
||||||
import org.eclipse.aether.impl.DefaultServiceLocator; |
|
||||||
import org.eclipse.aether.internal.impl.DefaultRepositorySystem; |
|
||||||
import org.eclipse.aether.repository.LocalRepository; |
|
||||||
import org.eclipse.aether.repository.RemoteRepository; |
|
||||||
import org.eclipse.aether.resolution.ArtifactDescriptorException; |
|
||||||
import org.eclipse.aether.resolution.ArtifactDescriptorRequest; |
|
||||||
import org.eclipse.aether.resolution.ArtifactDescriptorResult; |
|
||||||
import org.eclipse.aether.resolution.ArtifactResolutionException; |
|
||||||
import org.eclipse.aether.resolution.ArtifactResult; |
|
||||||
import org.eclipse.aether.resolution.DependencyRequest; |
|
||||||
import org.eclipse.aether.resolution.DependencyResult; |
|
||||||
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; |
|
||||||
import org.eclipse.aether.spi.connector.transport.TransporterFactory; |
|
||||||
import org.eclipse.aether.transfer.AbstractTransferListener; |
|
||||||
import org.eclipse.aether.transfer.TransferCancelledException; |
|
||||||
import org.eclipse.aether.transfer.TransferEvent; |
|
||||||
import org.eclipse.aether.transport.file.FileTransporterFactory; |
|
||||||
import org.eclipse.aether.transport.http.HttpTransporterFactory; |
|
||||||
import org.eclipse.aether.util.artifact.JavaScopes; |
|
||||||
import org.eclipse.aether.util.filter.DependencyFilterUtils; |
|
||||||
|
|
||||||
/** |
|
||||||
* A {@link GrapeEngine} implementation that uses <a |
|
||||||
* href="http://eclipse.org/aether">Aether</a>, the dependency resolution system used by |
|
||||||
* Maven. |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
@SuppressWarnings("rawtypes") |
|
||||||
public class AetherGrapeEngine implements GrapeEngine { |
|
||||||
|
|
||||||
private static final String DEPENDENCY_MODULE = "module"; |
|
||||||
|
|
||||||
private static final String DEPENDENCY_GROUP = "group"; |
|
||||||
|
|
||||||
private static final String DEPENDENCY_VERSION = "version"; |
|
||||||
|
|
||||||
private static final Collection<Exclusion> WILDCARD_EXCLUSION = Arrays |
|
||||||
.asList(new Exclusion("*", "*", "*", "*")); |
|
||||||
|
|
||||||
private final Artifact parentArtifact; |
|
||||||
|
|
||||||
private final ProgressReporter progressReporter = new ProgressReporter(); |
|
||||||
|
|
||||||
private final ArtifactDescriptorReader artifactDescriptorReader; |
|
||||||
|
|
||||||
private final GroovyClassLoader defaultClassLoader; |
|
||||||
|
|
||||||
private final RepositorySystemSession repositorySystemSession; |
|
||||||
|
|
||||||
private final RepositorySystem repositorySystem; |
|
||||||
|
|
||||||
private final List<RemoteRepository> repositories; |
|
||||||
|
|
||||||
public AetherGrapeEngine(GroovyClassLoader classLoader, String parentGroupId, |
|
||||||
String parentArtifactId, String parentVersion) { |
|
||||||
this.defaultClassLoader = classLoader; |
|
||||||
this.parentArtifact = new DefaultArtifact(parentGroupId, parentArtifactId, "pom", |
|
||||||
parentVersion); |
|
||||||
|
|
||||||
DefaultServiceLocator mavenServiceLocator = MavenRepositorySystemUtils |
|
||||||
.newServiceLocator(); |
|
||||||
mavenServiceLocator.addService(RepositorySystem.class, |
|
||||||
DefaultRepositorySystem.class); |
|
||||||
|
|
||||||
mavenServiceLocator.addService(RepositoryConnectorFactory.class, |
|
||||||
BasicRepositoryConnectorFactory.class); |
|
||||||
|
|
||||||
mavenServiceLocator.addService(TransporterFactory.class, |
|
||||||
HttpTransporterFactory.class); |
|
||||||
mavenServiceLocator.addService(TransporterFactory.class, |
|
||||||
FileTransporterFactory.class); |
|
||||||
|
|
||||||
this.repositorySystem = mavenServiceLocator.getService(RepositorySystem.class); |
|
||||||
|
|
||||||
DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils |
|
||||||
.newSession(); |
|
||||||
repositorySystemSession.setTransferListener(new AbstractTransferListener() { |
|
||||||
|
|
||||||
@Override |
|
||||||
public void transferStarted(TransferEvent event) |
|
||||||
throws TransferCancelledException { |
|
||||||
AetherGrapeEngine.this.progressReporter.reportProgress(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void transferProgressed(TransferEvent event) |
|
||||||
throws TransferCancelledException { |
|
||||||
AetherGrapeEngine.this.progressReporter.reportProgress(); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
repositorySystemSession.setRepositoryListener(new AbstractRepositoryListener() { |
|
||||||
@Override |
|
||||||
public void artifactResolved(RepositoryEvent event) { |
|
||||||
AetherGrapeEngine.this.progressReporter.reportProgress(); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
String grapeRootProperty = System.getProperty("grape.root"); |
|
||||||
File root; |
|
||||||
if (grapeRootProperty != null && grapeRootProperty.trim().length() > 0) { |
|
||||||
root = new File(grapeRootProperty); |
|
||||||
|
|
||||||
} |
|
||||||
else { |
|
||||||
root = new File(System.getProperty("user.home"), ".m2"); |
|
||||||
} |
|
||||||
|
|
||||||
LocalRepository localRepo = new LocalRepository(new File(root, "repository")); |
|
||||||
|
|
||||||
repositorySystemSession.setLocalRepositoryManager(this.repositorySystem |
|
||||||
.newLocalRepositoryManager(repositorySystemSession, localRepo)); |
|
||||||
|
|
||||||
this.repositorySystemSession = repositorySystemSession; |
|
||||||
|
|
||||||
List<RemoteRepository> repositories = new ArrayList<RemoteRepository>(); |
|
||||||
repositories.add(new RemoteRepository.Builder("central", "default", |
|
||||||
"http://repo1.maven.org/maven2/").build()); |
|
||||||
|
|
||||||
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) { |
|
||||||
repositories.add(new RemoteRepository.Builder("spring-snapshot", "default", |
|
||||||
"http://repo.spring.io/snapshot").build()); |
|
||||||
repositories.add(new RemoteRepository.Builder("spring-milestone", "default", |
|
||||||
"http://repo.spring.io/milestone").build()); |
|
||||||
} |
|
||||||
|
|
||||||
this.repositories = repositories; |
|
||||||
|
|
||||||
this.artifactDescriptorReader = mavenServiceLocator |
|
||||||
.getService(ArtifactDescriptorReader.class); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object grab(Map args) { |
|
||||||
return grab(args, args); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object grab(Map args, Map... dependencyMaps) { |
|
||||||
List<Dependency> dependencies = createDependencies(dependencyMaps); |
|
||||||
|
|
||||||
try { |
|
||||||
List<File> files = resolve(dependencies); |
|
||||||
GroovyClassLoader classLoader = (GroovyClassLoader) args.get("classLoader"); |
|
||||||
if (classLoader == null) { |
|
||||||
classLoader = this.defaultClassLoader; |
|
||||||
} |
|
||||||
for (File file : files) { |
|
||||||
classLoader.addURL(file.toURI().toURL()); |
|
||||||
} |
|
||||||
} |
|
||||||
catch (ArtifactResolutionException ex) { |
|
||||||
throw new DependencyResolutionFailedException(ex); |
|
||||||
} |
|
||||||
catch (MalformedURLException ex) { |
|
||||||
throw new DependencyResolutionFailedException(ex); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
private List<Dependency> createDependencies(Map<?, ?>... dependencyMaps) { |
|
||||||
List<Dependency> dependencies = new ArrayList<Dependency>(dependencyMaps.length); |
|
||||||
for (Map<?, ?> dependencyMap : dependencyMaps) { |
|
||||||
dependencies.add(createDependency(dependencyMap)); |
|
||||||
} |
|
||||||
return dependencies; |
|
||||||
} |
|
||||||
|
|
||||||
private boolean isTransitive(Map<?, ?> dependencyMap) { |
|
||||||
Boolean transitive = (Boolean) dependencyMap.get("transitive"); |
|
||||||
if (transitive == null) { |
|
||||||
transitive = true; |
|
||||||
} |
|
||||||
return transitive; |
|
||||||
} |
|
||||||
|
|
||||||
private Dependency createDependency(Map<?, ?> dependencyMap) { |
|
||||||
Artifact artifact = createArtifact(dependencyMap); |
|
||||||
|
|
||||||
Dependency dependency; |
|
||||||
|
|
||||||
if (!isTransitive(dependencyMap)) { |
|
||||||
dependency = new Dependency(artifact, JavaScopes.COMPILE, null, |
|
||||||
WILDCARD_EXCLUSION); |
|
||||||
} |
|
||||||
else { |
|
||||||
dependency = new Dependency(artifact, JavaScopes.COMPILE); |
|
||||||
} |
|
||||||
|
|
||||||
return dependency; |
|
||||||
} |
|
||||||
|
|
||||||
private Artifact createArtifact(Map<?, ?> dependencyMap) { |
|
||||||
String group = (String) dependencyMap.get(DEPENDENCY_GROUP); |
|
||||||
String module = (String) dependencyMap.get(DEPENDENCY_MODULE); |
|
||||||
String version = (String) dependencyMap.get(DEPENDENCY_VERSION); |
|
||||||
|
|
||||||
return new DefaultArtifact(group, module, "jar", version); |
|
||||||
} |
|
||||||
|
|
||||||
private List<File> resolve(List<Dependency> dependencies) |
|
||||||
throws ArtifactResolutionException { |
|
||||||
|
|
||||||
CollectRequest collectRequest = new CollectRequest((Dependency) null, |
|
||||||
dependencies, this.repositories); |
|
||||||
collectRequest.setManagedDependencies(getManagedDependencies()); |
|
||||||
|
|
||||||
try { |
|
||||||
DependencyResult dependencyResult = this.repositorySystem |
|
||||||
.resolveDependencies( |
|
||||||
this.repositorySystemSession, |
|
||||||
new DependencyRequest(collectRequest, DependencyFilterUtils |
|
||||||
.classpathFilter(JavaScopes.COMPILE))); |
|
||||||
List<File> files = new ArrayList<File>(); |
|
||||||
for (ArtifactResult result : dependencyResult.getArtifactResults()) { |
|
||||||
files.add(result.getArtifact().getFile()); |
|
||||||
} |
|
||||||
|
|
||||||
return files; |
|
||||||
} |
|
||||||
catch (Exception ex) { |
|
||||||
throw new DependencyResolutionFailedException(ex); |
|
||||||
} |
|
||||||
finally { |
|
||||||
this.progressReporter.finished(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private List<Dependency> getManagedDependencies() { |
|
||||||
ArtifactDescriptorRequest parentRequest = new ArtifactDescriptorRequest(); |
|
||||||
parentRequest.setArtifact(this.parentArtifact); |
|
||||||
|
|
||||||
try { |
|
||||||
ArtifactDescriptorResult artifactDescriptorResult = this.artifactDescriptorReader |
|
||||||
.readArtifactDescriptor(this.repositorySystemSession, parentRequest); |
|
||||||
return artifactDescriptorResult.getManagedDependencies(); |
|
||||||
} |
|
||||||
catch (ArtifactDescriptorException ex) { |
|
||||||
throw new DependencyResolutionFailedException(ex); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Map<String, Map<String, List<String>>> enumerateGrapes() { |
|
||||||
throw new UnsupportedOperationException("Grape enumeration is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public URI[] resolve(Map args, Map... dependencies) { |
|
||||||
throw new UnsupportedOperationException("Resolving to URIs is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public URI[] resolve(Map args, List depsInfo, Map... dependencies) { |
|
||||||
throw new UnsupportedOperationException("Resolving to URIs is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Map[] listDependencies(ClassLoader classLoader) { |
|
||||||
throw new UnsupportedOperationException("Listing dependencies is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void addResolver(Map<String, Object> args) { |
|
||||||
throw new UnsupportedOperationException("Adding a resolver is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object grab(String endorsedModule) { |
|
||||||
throw new UnsupportedOperationException( |
|
||||||
"Grabbing an endorsed module is not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
private static final class ProgressReporter { |
|
||||||
|
|
||||||
private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3); |
|
||||||
|
|
||||||
private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1); |
|
||||||
|
|
||||||
private long startTime = System.currentTimeMillis(); |
|
||||||
|
|
||||||
private long lastProgressTime = System.currentTimeMillis(); |
|
||||||
|
|
||||||
private boolean started; |
|
||||||
|
|
||||||
private boolean finished; |
|
||||||
|
|
||||||
void reportProgress() { |
|
||||||
if (!this.finished |
|
||||||
&& System.currentTimeMillis() - this.startTime > INITIAL_DELAY) { |
|
||||||
if (!this.started) { |
|
||||||
this.started = true; |
|
||||||
System.out.print("Resolving dependencies.."); |
|
||||||
this.lastProgressTime = System.currentTimeMillis(); |
|
||||||
} |
|
||||||
else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) { |
|
||||||
System.out.print("."); |
|
||||||
this.lastProgressTime = System.currentTimeMillis(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void finished() { |
|
||||||
if (this.started && !this.finished) { |
|
||||||
this.finished = true; |
|
||||||
System.out.println(""); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,26 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<assembly |
||||||
|
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> |
||||||
|
<id>full</id> |
||||||
|
<formats> |
||||||
|
<format>jar</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<useProjectArtifact /> |
||||||
|
<includes> |
||||||
|
<include>${project.groupId}:${project.artifactId}</include> |
||||||
|
</includes> |
||||||
|
<unpack>true</unpack> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.build.directory}/assembly</directory> |
||||||
|
<outputDirectory>/</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</assembly> |
||||||
@ -1,25 +0,0 @@ |
|||||||
<assembly> |
|
||||||
<id>repackaged</id> |
|
||||||
<formats> |
|
||||||
<format>jar</format> |
|
||||||
</formats> |
|
||||||
<includeBaseDirectory>false</includeBaseDirectory> |
|
||||||
<dependencySets> |
|
||||||
<dependencySet> |
|
||||||
<includes> |
|
||||||
<include>org.springframework.boot:spring-boot-cli:jar:*</include> |
|
||||||
</includes> |
|
||||||
<unpack>true</unpack> |
|
||||||
<directoryMode>755</directoryMode> |
|
||||||
</dependencySet> |
|
||||||
<dependencySet> |
|
||||||
<includes> |
|
||||||
<include>org.springframework.boot:spring-boot-cli-grape:jar:*</include> |
|
||||||
</includes> |
|
||||||
<outputDirectory>internal</outputDirectory> |
|
||||||
<directoryMode>755</directoryMode> |
|
||||||
<scope>provided</scope> |
|
||||||
<unpack>true</unpack> |
|
||||||
</dependencySet> |
|
||||||
</dependencySets> |
|
||||||
</assembly> |
|
||||||
@ -1,71 +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. |
|
||||||
*/ |
|
||||||
|
|
||||||
import org.junit.runner.JUnitCore |
|
||||||
import org.junit.runner.Result |
|
||||||
import org.springframework.boot.cli.command.tester.Failure |
|
||||||
import org.springframework.boot.cli.command.tester.TestResults |
|
||||||
|
|
||||||
import java.lang.annotation.Annotation |
|
||||||
import java.lang.reflect.Method |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Groovy script to run JUnit tests inside the {@link TestCommand}. |
|
||||||
* Needs to be compiled along with the actual code to work properly. |
|
||||||
* |
|
||||||
* @author Greg Turnquist |
|
||||||
*/ |
|
||||||
class JUnitTester extends AbstractTester { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected Set<Class<?>> findTestableClasses(List<Class<?>> compiled) { |
|
||||||
// Look for @Test methods |
|
||||||
Set<Class<?>> testable = new LinkedHashSet<Class<?>>() |
|
||||||
for (Class<?> clazz : compiled) { |
|
||||||
for (Method method : clazz.getMethods()) { |
|
||||||
for (Annotation annotation : method.getAnnotations()) { |
|
||||||
if (annotation.toString().contains("Test")) { |
|
||||||
testable.add(clazz) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return testable |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected TestResults test(List<Class<?>> testable) { |
|
||||||
return JUnitTester.runEmbeddedTests(testable) |
|
||||||
} |
|
||||||
|
|
||||||
static TestResults runEmbeddedTests(List<Class<?>> testable) { |
|
||||||
Result results = JUnitCore.runClasses(testable.toArray(new Class<?>[0])) |
|
||||||
|
|
||||||
TestResults testResults = new TestResults() |
|
||||||
testResults.setRunCount(results.getRunCount()) |
|
||||||
|
|
||||||
List<Failure> failures = new ArrayList<Failure>() |
|
||||||
for (org.junit.runner.notification.Failure failure : results.getFailures()) { |
|
||||||
failures.add(new Failure(failure.exception.toString(), failure.trace)) |
|
||||||
} |
|
||||||
|
|
||||||
testResults.setFailures(failures) |
|
||||||
|
|
||||||
return testResults |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,47 +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. |
|
||||||
*/ |
|
||||||
|
|
||||||
import org.springframework.boot.cli.command.tester.TestResults |
|
||||||
import spock.lang.Specification |
|
||||||
|
|
||||||
/** |
|
||||||
* Groovy script to run Spock tests inside the {@link TestCommand}. |
|
||||||
* Needs to be compiled along with the actual code to work properly. |
|
||||||
* |
|
||||||
* NOTE: SpockTester depends on JUnitTester to actually run the tests |
|
||||||
* |
|
||||||
* @author Greg Turnquist |
|
||||||
*/ |
|
||||||
class SpockTester extends AbstractTester { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected Set<Class<?>> findTestableClasses(List<Class<?>> compiled) { |
|
||||||
// Look for classes that implement spock.lang.Specification |
|
||||||
Set<Class<?>> testable = new LinkedHashSet<Class<?>>() |
|
||||||
for (Class<?> clazz : compiled) { |
|
||||||
if (Specification.class.isAssignableFrom(clazz)) { |
|
||||||
testable.add(clazz) |
|
||||||
} |
|
||||||
} |
|
||||||
return testable |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected TestResults test(List<Class<?>> testable) { |
|
||||||
return JUnitTester.runEmbeddedTests(testable) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,42 +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. |
|
||||||
*/ |
|
||||||
|
|
||||||
import org.springframework.boot.cli.command.tester.TestResults |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Groovy script define abstract basis for automated testers for {@link TestCommand}. |
|
||||||
* Needs to be compiled along with the actual code to work properly. |
|
||||||
* |
|
||||||
* @author Greg Turnquist |
|
||||||
*/ |
|
||||||
public abstract class AbstractTester { |
|
||||||
|
|
||||||
public TestResults findAndTest(List<Class<?>> compiled) throws FileNotFoundException { |
|
||||||
Set<Class<?>> testable = findTestableClasses(compiled) |
|
||||||
|
|
||||||
if (testable.size() == 0) { |
|
||||||
return TestResults.NONE |
|
||||||
} |
|
||||||
|
|
||||||
return test(new ArrayList<Class<?>>(testable)) |
|
||||||
} |
|
||||||
|
|
||||||
protected abstract Set<Class<?>> findTestableClasses(List<Class<?>> compiled) |
|
||||||
|
|
||||||
protected abstract TestResults test(List<Class<?>> testable) |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,52 +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.tester; |
|
||||||
|
|
||||||
/** |
|
||||||
* Platform neutral way to capture a test failure |
|
||||||
* |
|
||||||
* NOTE: This is needed to avoid having to add JUnit jar file to the deployable artifacts |
|
||||||
* |
|
||||||
* @author Greg Turnquist |
|
||||||
*/ |
|
||||||
public class Failure { |
|
||||||
|
|
||||||
private String description; |
|
||||||
|
|
||||||
private String trace; |
|
||||||
|
|
||||||
public Failure(String description, String trace) { |
|
||||||
this.description = description; |
|
||||||
this.trace = trace; |
|
||||||
} |
|
||||||
|
|
||||||
public String getDescription() { |
|
||||||
return this.description; |
|
||||||
} |
|
||||||
|
|
||||||
public void setDescription(String description) { |
|
||||||
this.description = description; |
|
||||||
} |
|
||||||
|
|
||||||
public String getTrace() { |
|
||||||
return this.trace; |
|
||||||
} |
|
||||||
|
|
||||||
public void setTrace(String trace) { |
|
||||||
this.trace = trace; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,87 +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.tester; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* Platform neutral way to collect test results |
|
||||||
* |
|
||||||
* NOTE: This is needed to avoid having to add JUnit's jar file to the deployable |
|
||||||
* artifacts |
|
||||||
* |
|
||||||
* @author Greg Turnquist |
|
||||||
*/ |
|
||||||
public class TestResults { |
|
||||||
|
|
||||||
public static final TestResults NONE = new TestResults() { |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getRunCount() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getFailureCount() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public List<Failure> getFailures() { |
|
||||||
return new ArrayList<Failure>(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean wasSuccessful() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
}; |
|
||||||
private int runCount = 0; |
|
||||||
private List<Failure> failures = new ArrayList<Failure>(); |
|
||||||
|
|
||||||
public void add(TestResults results) { |
|
||||||
this.runCount += results.getRunCount(); |
|
||||||
this.failures.addAll(results.getFailures()); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean wasSuccessful() { |
|
||||||
return this.failures.size() == 0; |
|
||||||
} |
|
||||||
|
|
||||||
public int getRunCount() { |
|
||||||
return this.runCount; |
|
||||||
} |
|
||||||
|
|
||||||
public void setRunCount(int runCount) { |
|
||||||
this.runCount = runCount; |
|
||||||
} |
|
||||||
|
|
||||||
public int getFailureCount() { |
|
||||||
return this.failures.size(); |
|
||||||
} |
|
||||||
|
|
||||||
public List<Failure> getFailures() { |
|
||||||
return this.failures; |
|
||||||
} |
|
||||||
|
|
||||||
public void setFailures(List<Failure> failures) { |
|
||||||
this.failures = failures; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,37 @@ |
|||||||
|
/* |
||||||
|
* 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.compiler; |
||||||
|
|
||||||
|
/** |
||||||
|
* The scope in which a groovy compiler operates. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public enum GroovyCompilerScope { |
||||||
|
|
||||||
|
/** |
||||||
|
* Default scope, exposes groovy-all.jar (loaded from the parent) and the shared cli |
||||||
|
* package (loaded via groovy classloader). |
||||||
|
*/ |
||||||
|
DEFAULT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Extension scope, allows full access to internal CLI classes. |
||||||
|
*/ |
||||||
|
EXTENSION |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,249 @@ |
|||||||
|
/* |
||||||
|
* 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.compiler.grape; |
||||||
|
|
||||||
|
import groovy.grape.GrapeEngine; |
||||||
|
import groovy.lang.GroovyClassLoader; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.net.MalformedURLException; |
||||||
|
import java.net.URI; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.apache.maven.repository.internal.MavenRepositorySystemUtils; |
||||||
|
import org.eclipse.aether.DefaultRepositorySystemSession; |
||||||
|
import org.eclipse.aether.RepositorySystem; |
||||||
|
import org.eclipse.aether.RepositorySystemSession; |
||||||
|
import org.eclipse.aether.artifact.Artifact; |
||||||
|
import org.eclipse.aether.artifact.DefaultArtifact; |
||||||
|
import org.eclipse.aether.collection.CollectRequest; |
||||||
|
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; |
||||||
|
import org.eclipse.aether.graph.Dependency; |
||||||
|
import org.eclipse.aether.graph.Exclusion; |
||||||
|
import org.eclipse.aether.impl.DefaultServiceLocator; |
||||||
|
import org.eclipse.aether.internal.impl.DefaultRepositorySystem; |
||||||
|
import org.eclipse.aether.repository.LocalRepository; |
||||||
|
import org.eclipse.aether.repository.LocalRepositoryManager; |
||||||
|
import org.eclipse.aether.repository.RemoteRepository; |
||||||
|
import org.eclipse.aether.resolution.ArtifactResolutionException; |
||||||
|
import org.eclipse.aether.resolution.ArtifactResult; |
||||||
|
import org.eclipse.aether.resolution.DependencyRequest; |
||||||
|
import org.eclipse.aether.resolution.DependencyResult; |
||||||
|
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; |
||||||
|
import org.eclipse.aether.spi.connector.transport.TransporterFactory; |
||||||
|
import org.eclipse.aether.spi.locator.ServiceLocator; |
||||||
|
import org.eclipse.aether.transport.file.FileTransporterFactory; |
||||||
|
import org.eclipse.aether.transport.http.HttpTransporterFactory; |
||||||
|
import org.eclipse.aether.util.artifact.JavaScopes; |
||||||
|
import org.eclipse.aether.util.filter.DependencyFilterUtils; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link GrapeEngine} implementation that uses <a |
||||||
|
* href="http://eclipse.org/aether">Aether</a>, the dependency resolution system used by |
||||||
|
* Maven. |
||||||
|
* |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
@SuppressWarnings("rawtypes") |
||||||
|
public class AetherGrapeEngine implements GrapeEngine { |
||||||
|
|
||||||
|
private static final Collection<Exclusion> WILDCARD_EXCLUSION = Arrays |
||||||
|
.asList(new Exclusion("*", "*", "*", "*")); |
||||||
|
|
||||||
|
private final ProgressReporter progressReporter; |
||||||
|
|
||||||
|
private final GroovyClassLoader classLoader; |
||||||
|
|
||||||
|
private final RepositorySystemSession session; |
||||||
|
|
||||||
|
private final RepositorySystem repositorySystem; |
||||||
|
|
||||||
|
private final List<RemoteRepository> repositories; |
||||||
|
|
||||||
|
public AetherGrapeEngine(GroovyClassLoader classLoader) { |
||||||
|
this.classLoader = classLoader; |
||||||
|
this.repositorySystem = createServiceLocator().getService(RepositorySystem.class); |
||||||
|
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); |
||||||
|
LocalRepository localRepository = new LocalRepository(getM2RepoDirectory()); |
||||||
|
LocalRepositoryManager localRepositoryManager = this.repositorySystem |
||||||
|
.newLocalRepositoryManager(session, localRepository); |
||||||
|
session.setLocalRepositoryManager(localRepositoryManager); |
||||||
|
this.session = session; |
||||||
|
this.repositories = getRemoteRepositories(); |
||||||
|
this.progressReporter = new ProgressReporter(session); |
||||||
|
} |
||||||
|
|
||||||
|
private ServiceLocator createServiceLocator() { |
||||||
|
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); |
||||||
|
locator.addService(RepositorySystem.class, DefaultRepositorySystem.class); |
||||||
|
locator.addService(RepositoryConnectorFactory.class, |
||||||
|
BasicRepositoryConnectorFactory.class); |
||||||
|
locator.addService(TransporterFactory.class, HttpTransporterFactory.class); |
||||||
|
locator.addService(TransporterFactory.class, FileTransporterFactory.class); |
||||||
|
return locator; |
||||||
|
} |
||||||
|
|
||||||
|
private File getM2RepoDirectory() { |
||||||
|
return new File(getM2HomeDirectory(), "repository"); |
||||||
|
} |
||||||
|
|
||||||
|
private File getM2HomeDirectory() { |
||||||
|
String grapeRoot = System.getProperty("grape.root"); |
||||||
|
if (StringUtils.hasLength(grapeRoot)) { |
||||||
|
return new File(grapeRoot); |
||||||
|
} |
||||||
|
return new File(System.getProperty("user.home"), ".m2"); |
||||||
|
} |
||||||
|
|
||||||
|
private List<RemoteRepository> getRemoteRepositories() { |
||||||
|
List<RemoteRepository> repositories = new ArrayList<RemoteRepository>(); |
||||||
|
addRemoteRepository(repositories, "central", "http://repo1.maven.org/maven2/"); |
||||||
|
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) { |
||||||
|
addRemoteRepository(repositories, "spring-snapshot", |
||||||
|
"http://repo.spring.io/snapshot"); |
||||||
|
addRemoteRepository(repositories, "spring-milestone", |
||||||
|
"http://repo.spring.io/milestone"); |
||||||
|
} |
||||||
|
return repositories; |
||||||
|
} |
||||||
|
|
||||||
|
private void addRemoteRepository(List<RemoteRepository> repositories, String id, |
||||||
|
String url) { |
||||||
|
repositories.add(new RemoteRepository.Builder(id, "default", url).build()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object grab(Map args) { |
||||||
|
return grab(args, args); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object grab(Map args, Map... dependencyMaps) { |
||||||
|
try { |
||||||
|
List<Dependency> dependencies = createDependencies(dependencyMaps); |
||||||
|
List<File> files = resolve(dependencies); |
||||||
|
GroovyClassLoader classLoader = getClassLoader(args); |
||||||
|
for (File file : files) { |
||||||
|
classLoader.addURL(file.toURI().toURL()); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (ArtifactResolutionException ex) { |
||||||
|
throw new DependencyResolutionFailedException(ex); |
||||||
|
} |
||||||
|
catch (MalformedURLException ex) { |
||||||
|
throw new DependencyResolutionFailedException(ex); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private GroovyClassLoader getClassLoader(Map args) { |
||||||
|
GroovyClassLoader classLoader = (GroovyClassLoader) args.get("classLoader"); |
||||||
|
return (classLoader == null ? this.classLoader : classLoader); |
||||||
|
} |
||||||
|
|
||||||
|
private List<Dependency> createDependencies(Map<?, ?>... dependencyMaps) { |
||||||
|
List<Dependency> dependencies = new ArrayList<Dependency>(dependencyMaps.length); |
||||||
|
for (Map<?, ?> dependencyMap : dependencyMaps) { |
||||||
|
dependencies.add(createDependency(dependencyMap)); |
||||||
|
} |
||||||
|
return dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
private Dependency createDependency(Map<?, ?> dependencyMap) { |
||||||
|
Artifact artifact = createArtifact(dependencyMap); |
||||||
|
if (isTransitive(dependencyMap)) { |
||||||
|
return new Dependency(artifact, JavaScopes.COMPILE); |
||||||
|
} |
||||||
|
return new Dependency(artifact, JavaScopes.COMPILE, null, WILDCARD_EXCLUSION); |
||||||
|
} |
||||||
|
|
||||||
|
private Artifact createArtifact(Map<?, ?> dependencyMap) { |
||||||
|
String group = (String) dependencyMap.get("group"); |
||||||
|
String module = (String) dependencyMap.get("module"); |
||||||
|
String version = (String) dependencyMap.get("version"); |
||||||
|
return new DefaultArtifact(group, module, "jar", version); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isTransitive(Map<?, ?> dependencyMap) { |
||||||
|
Boolean transitive = (Boolean) dependencyMap.get("transitive"); |
||||||
|
return (transitive == null ? true : transitive); |
||||||
|
} |
||||||
|
|
||||||
|
private List<File> resolve(List<Dependency> dependencies) |
||||||
|
throws ArtifactResolutionException { |
||||||
|
try { |
||||||
|
CollectRequest collectRequest = new CollectRequest((Dependency) null, |
||||||
|
dependencies, this.repositories); |
||||||
|
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, |
||||||
|
DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE)); |
||||||
|
DependencyResult dependencyResult = this.repositorySystem |
||||||
|
.resolveDependencies(this.session, dependencyRequest); |
||||||
|
return getFiles(dependencyResult); |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new DependencyResolutionFailedException(ex); |
||||||
|
} |
||||||
|
finally { |
||||||
|
this.progressReporter.finished(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private List<File> getFiles(DependencyResult dependencyResult) { |
||||||
|
List<File> files = new ArrayList<File>(); |
||||||
|
for (ArtifactResult result : dependencyResult.getArtifactResults()) { |
||||||
|
files.add(result.getArtifact().getFile()); |
||||||
|
} |
||||||
|
return files; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Map<String, Map<String, List<String>>> enumerateGrapes() { |
||||||
|
throw new UnsupportedOperationException("Grape enumeration is not supported"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public URI[] resolve(Map args, Map... dependencies) { |
||||||
|
throw new UnsupportedOperationException("Resolving to URIs is not supported"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public URI[] resolve(Map args, List depsInfo, Map... dependencies) { |
||||||
|
throw new UnsupportedOperationException("Resolving to URIs is not supported"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Map[] listDependencies(ClassLoader classLoader) { |
||||||
|
throw new UnsupportedOperationException("Listing dependencies is not supported"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addResolver(Map<String, Object> args) { |
||||||
|
throw new UnsupportedOperationException("Adding a resolver is not supported"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object grab(String endorsedModule) { |
||||||
|
throw new UnsupportedOperationException( |
||||||
|
"Grabbing an endorsed module is not supported"); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* 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.compiler.grape; |
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
import org.eclipse.aether.AbstractRepositoryListener; |
||||||
|
import org.eclipse.aether.DefaultRepositorySystemSession; |
||||||
|
import org.eclipse.aether.RepositoryEvent; |
||||||
|
import org.eclipse.aether.transfer.AbstractTransferListener; |
||||||
|
import org.eclipse.aether.transfer.TransferCancelledException; |
||||||
|
import org.eclipse.aether.transfer.TransferEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Provide console progress feedback for long running resolves. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
* @author Andy Wilkinson |
||||||
|
*/ |
||||||
|
final class ProgressReporter { |
||||||
|
|
||||||
|
private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3); |
||||||
|
|
||||||
|
private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1); |
||||||
|
|
||||||
|
private long startTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
private long lastProgressTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
private boolean started; |
||||||
|
|
||||||
|
private boolean finished; |
||||||
|
|
||||||
|
public ProgressReporter(DefaultRepositorySystemSession session) { |
||||||
|
session.setTransferListener(new AbstractTransferListener() { |
||||||
|
@Override |
||||||
|
public void transferStarted(TransferEvent event) |
||||||
|
throws TransferCancelledException { |
||||||
|
reportProgress(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void transferProgressed(TransferEvent event) |
||||||
|
throws TransferCancelledException { |
||||||
|
reportProgress(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
session.setRepositoryListener(new AbstractRepositoryListener() { |
||||||
|
@Override |
||||||
|
public void artifactResolved(RepositoryEvent event) { |
||||||
|
reportProgress(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void reportProgress() { |
||||||
|
if (!this.finished && System.currentTimeMillis() - this.startTime > INITIAL_DELAY) { |
||||||
|
if (!this.started) { |
||||||
|
this.started = true; |
||||||
|
System.out.print("Resolving dependencies.."); |
||||||
|
this.lastProgressTime = System.currentTimeMillis(); |
||||||
|
} |
||||||
|
else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) { |
||||||
|
System.out.print("."); |
||||||
|
this.lastProgressTime = System.currentTimeMillis(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void finished() { |
||||||
|
if (this.started && !this.finished) { |
||||||
|
this.finished = true; |
||||||
|
System.out.println(""); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,149 @@ |
|||||||
|
/* |
||||||
|
* 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.testrunner; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.lang.annotation.Annotation; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.boot.cli.compiler.GroovyCompiler; |
||||||
|
import org.springframework.boot.groovy.DelegateTestRunner; |
||||||
|
|
||||||
|
/** |
||||||
|
* Compile and run groovy based tests. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public class TestRunner { |
||||||
|
|
||||||
|
private static final String DELEGATE_RUNNER = DelegateTestRunner.class.getName(); |
||||||
|
|
||||||
|
private static final String JUNIT_TEST_ANNOTATION = "org.junit.Test"; |
||||||
|
|
||||||
|
private final File[] files; |
||||||
|
|
||||||
|
private final GroovyCompiler compiler; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new {@link TestRunner} instance. |
||||||
|
* @param configuration |
||||||
|
* @param files |
||||||
|
* @param args |
||||||
|
*/ |
||||||
|
public TestRunner(TestRunnerConfiguration configuration, File[] files, String[] args) { |
||||||
|
this.files = files.clone(); |
||||||
|
this.compiler = new GroovyCompiler(configuration); |
||||||
|
} |
||||||
|
|
||||||
|
public void compileAndRunTests() throws Exception { |
||||||
|
Object[] sources = this.compiler.sources(this.files); |
||||||
|
if (sources.length == 0) { |
||||||
|
throw new RuntimeException("No classes found in '" + this.files + "'"); |
||||||
|
} |
||||||
|
|
||||||
|
// Run in new thread to ensure that the context classloader is setup
|
||||||
|
RunThread runThread = new RunThread(sources); |
||||||
|
runThread.start(); |
||||||
|
runThread.join(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Thread used to launch the Spring Application with the correct context classloader. |
||||||
|
*/ |
||||||
|
private class RunThread extends Thread { |
||||||
|
|
||||||
|
private final Class<?>[] testClasses; |
||||||
|
|
||||||
|
private final Class<?> spockSpecificationClass; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new {@link RunThread} instance. |
||||||
|
* @param sources the sources to launch |
||||||
|
*/ |
||||||
|
public RunThread(Object... sources) { |
||||||
|
super("testrunner"); |
||||||
|
setDaemon(true); |
||||||
|
if (sources.length != 0 && sources[0] instanceof Class) { |
||||||
|
setContextClassLoader(((Class<?>) sources[0]).getClassLoader()); |
||||||
|
} |
||||||
|
this.spockSpecificationClass = loadSpockSpecificationClass(getContextClassLoader()); |
||||||
|
this.testClasses = getTestClasses(sources); |
||||||
|
} |
||||||
|
|
||||||
|
private Class<?> loadSpockSpecificationClass(ClassLoader contextClassLoader) { |
||||||
|
try { |
||||||
|
return getContextClassLoader().loadClass("spock.lang.Specification"); |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Class<?>[] getTestClasses(Object[] sources) { |
||||||
|
List<Class<?>> testClasses = new ArrayList<Class<?>>(); |
||||||
|
for (Object source : sources) { |
||||||
|
if ((source instanceof Class) && isTestable((Class<?>) source)) { |
||||||
|
testClasses.add((Class<?>) source); |
||||||
|
} |
||||||
|
} |
||||||
|
return testClasses.toArray(new Class<?>[testClasses.size()]); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isTestable(Class<?> sourceClass) { |
||||||
|
return (isJunitTest(sourceClass) || isSpockTest(sourceClass)); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isJunitTest(Class<?> sourceClass) { |
||||||
|
for (Method method : sourceClass.getMethods()) { |
||||||
|
for (Annotation annotation : method.getAnnotations()) { |
||||||
|
if (annotation.annotationType().getName() |
||||||
|
.equals(JUNIT_TEST_ANNOTATION)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isSpockTest(Class<?> sourceClass) { |
||||||
|
return (this.spockSpecificationClass != null && this.spockSpecificationClass |
||||||
|
.isAssignableFrom(sourceClass)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try { |
||||||
|
if (this.testClasses.length == 0) { |
||||||
|
System.out.println("No tests found"); |
||||||
|
} |
||||||
|
else { |
||||||
|
Class<?> delegateClass = Thread.currentThread() |
||||||
|
.getContextClassLoader() |
||||||
|
.loadClass(DelegateTestRunner.class.getName()); |
||||||
|
Method runMethod = delegateClass.getMethod("run", Class[].class); |
||||||
|
runMethod.invoke(null, new Object[] { this.testClasses }); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
ex.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
/* |
||||||
|
* 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.testrunner; |
||||||
|
|
||||||
|
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Configuration for {@link TestRunner}. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public interface TestRunnerConfiguration extends GroovyCompilerConfiguration { |
||||||
|
|
||||||
|
} |
||||||
@ -1,95 +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.util; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.FileOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.io.OutputStream; |
|
||||||
import java.net.URL; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* File utility methods |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
public class FileUtils { |
|
||||||
|
|
||||||
private FileUtils() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Recursively deletes the given {@code file} and all files beneath it. |
|
||||||
* @param file The root of the structure to delete |
|
||||||
* @throw IllegalStateException if the delete fails |
|
||||||
*/ |
|
||||||
public static void recursiveDelete(File file) { |
|
||||||
if (file.exists()) { |
|
||||||
if (file.isDirectory()) { |
|
||||||
for (File inDir : file.listFiles()) { |
|
||||||
recursiveDelete(inDir); |
|
||||||
} |
|
||||||
} |
|
||||||
if (!file.delete()) { |
|
||||||
throw new IllegalStateException("Failed to delete " + file); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Lists the given {@code file} and all the files beneath it. |
|
||||||
* @param file The root of the structure to delete |
|
||||||
* @return The list of files and directories |
|
||||||
*/ |
|
||||||
public static List<File> recursiveList(File file) { |
|
||||||
List<File> files = new ArrayList<File>(); |
|
||||||
if (file.isDirectory()) { |
|
||||||
for (File inDir : file.listFiles()) { |
|
||||||
files.addAll(recursiveList(inDir)); |
|
||||||
} |
|
||||||
} |
|
||||||
files.add(file); |
|
||||||
return files; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Copies the data read from the given {@code source} {@link URL} to the given |
|
||||||
* {@code target} {@link File}. |
|
||||||
* @param source The source to copy from |
|
||||||
* @param target The target to copy to |
|
||||||
*/ |
|
||||||
public static void copy(URL source, File target) { |
|
||||||
InputStream input = null; |
|
||||||
OutputStream output = null; |
|
||||||
try { |
|
||||||
input = source.openStream(); |
|
||||||
output = new FileOutputStream(target); |
|
||||||
IoUtils.copy(input, output); |
|
||||||
} |
|
||||||
catch (IOException ex) { |
|
||||||
throw new IllegalStateException("Failed to copy '" + source + "' to '" |
|
||||||
+ target + "'", ex); |
|
||||||
} |
|
||||||
finally { |
|
||||||
IoUtils.closeQuietly(input, output); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,95 +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.util; |
|
||||||
|
|
||||||
import java.io.BufferedReader; |
|
||||||
import java.io.Closeable; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.io.InputStreamReader; |
|
||||||
import java.io.OutputStream; |
|
||||||
import java.net.URI; |
|
||||||
|
|
||||||
/** |
|
||||||
* General IO utility methods |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
public class IoUtils { |
|
||||||
|
|
||||||
private IoUtils() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads the entire contents of the resource referenced by {@code uri} and returns |
|
||||||
* them as a String. |
|
||||||
* @param uri The resource to read |
|
||||||
* @return The contents of the resource |
|
||||||
*/ |
|
||||||
public 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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Copies the data read from {@code input} into {@code output}. |
|
||||||
* <p> |
|
||||||
* <strong>Note:</strong> it is the caller's responsibility to close the streams |
|
||||||
* @param input The stream to read data from |
|
||||||
* @param output The stream to write data to |
|
||||||
* @throws IOException if the copy fails |
|
||||||
*/ |
|
||||||
public static void copy(InputStream input, OutputStream output) throws IOException { |
|
||||||
byte[] buffer = new byte[4096]; |
|
||||||
int read; |
|
||||||
while ((read = input.read(buffer)) >= 0) { |
|
||||||
output.write(buffer, 0, read); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Quietly closes the given {@link Closeable Closeables}. Any exceptions thrown by |
|
||||||
* {@link Closeable#close() close()} are swallowed. Any {@code null} |
|
||||||
* {@code Closeables} are ignored. |
|
||||||
* @param closeables The {@link Closeable closeables} to close |
|
||||||
*/ |
|
||||||
public static void closeQuietly(Closeable... closeables) { |
|
||||||
for (Closeable closeable : closeables) { |
|
||||||
if (closeable != null) { |
|
||||||
try { |
|
||||||
closeable.close(); |
|
||||||
} |
|
||||||
catch (IOException ioe) { |
|
||||||
// Closing quietly
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,37 @@ |
|||||||
|
/* |
||||||
|
* 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.groovy; |
||||||
|
|
||||||
|
import org.junit.internal.TextListener; |
||||||
|
import org.junit.runner.JUnitCore; |
||||||
|
import org.springframework.boot.cli.testrunner.TestRunner; |
||||||
|
|
||||||
|
/** |
||||||
|
* Delegate test runner to launch tests in user application classpath. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
* @see TestRunner |
||||||
|
*/ |
||||||
|
public class DelegateTestRunner { |
||||||
|
|
||||||
|
public static void run(Class<?>[] testClasses) { |
||||||
|
JUnitCore jUnitCore = new JUnitCore(); |
||||||
|
jUnitCore.addListener(new TextListener(System.out)); |
||||||
|
jUnitCore.run(testClasses); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* 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.groovy; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
@Target(ElementType.TYPE) |
||||||
|
@Documented |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface EnableDeviceResolver { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* 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.groovy; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
@Target(ElementType.TYPE) |
||||||
|
@Documented |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface EnableIntegrationPatterns { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* 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.groovy; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
@Target(ElementType.TYPE) |
||||||
|
@Documented |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface EnableJmsMessaging { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Groovy util classes that are "shared" between the CLI and user applications. Classes is |
||||||
|
* this package can be loaded from compiled user code. Not under the cli package in case |
||||||
|
* we want to extract into a separate jar at a future date. |
||||||
|
*/ |
||||||
|
package org.springframework.boot.groovy; |
||||||
|
|
||||||
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for CLI Classloader issues. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public class ClassLoaderIntegrationTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
public CliTester cli = new CliTester(); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void runWithIsolatedClassLoader() throws Exception { |
||||||
|
// CLI classes or dependencies should not be exposed to the app
|
||||||
|
String output = this.cli.run("src/test/resources/classloader-test-app.groovy", |
||||||
|
SpringCli.class.getName()); |
||||||
|
assertThat(output, containsString("HasClasses-false-true-false")); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* 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.Test; |
||||||
|
import org.junit.internal.TextListener; |
||||||
|
import org.junit.runner.JUnitCore; |
||||||
|
|
||||||
|
import static org.junit.Assert.fail; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author pwebb |
||||||
|
*/ |
||||||
|
public class TestTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testName() throws Exception { |
||||||
|
fail("Arse"); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
JUnitCore core = new JUnitCore(); |
||||||
|
core.addListener(new TextListener(System.out)); |
||||||
|
core.run(TestTest.class); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -1,129 +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 groovy.lang.GroovyObjectSupport; |
|
||||||
import groovy.lang.Script; |
|
||||||
|
|
||||||
import org.junit.BeforeClass; |
|
||||||
import org.junit.Test; |
|
||||||
import org.springframework.boot.cli.GrapesCleaner; |
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals; |
|
||||||
import static org.junit.Assert.assertNotSame; |
|
||||||
import static org.junit.Assert.assertTrue; |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests for {@link ScriptCommand}. |
|
||||||
* |
|
||||||
* @author Dave Syer |
|
||||||
*/ |
|
||||||
public class ScriptCommandTests { |
|
||||||
|
|
||||||
public static boolean executed = false; |
|
||||||
|
|
||||||
@BeforeClass |
|
||||||
public static void cleanGrapes() throws Exception { |
|
||||||
GrapesCleaner.cleanIfNecessary(); |
|
||||||
} |
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class) |
|
||||||
public void testMissing() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("missing"); |
|
||||||
command.run("World"); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testScript() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("script"); |
|
||||||
command.run("World"); |
|
||||||
assertEquals("World", |
|
||||||
((String[]) ((Script) command.getMain()).getProperty("args"))[0]); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testLocateFile() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand( |
|
||||||
"src/test/resources/commands/script.groovy"); |
|
||||||
command.setPaths(new String[] { "." }); |
|
||||||
command.run("World"); |
|
||||||
assertEquals("World", |
|
||||||
((String[]) ((Script) command.getMain()).getProperty("args"))[0]); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testRunnable() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("runnable"); |
|
||||||
command.run("World"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testClosure() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("closure"); |
|
||||||
command.run("World"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testCommand() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("command"); |
|
||||||
assertEquals("My script command", command.getUsageHelp()); |
|
||||||
command.run("World"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testDuplicateClassName() throws Exception { |
|
||||||
ScriptCommand command1 = new ScriptCommand("handler"); |
|
||||||
ScriptCommand command2 = new ScriptCommand("command"); |
|
||||||
assertNotSame(command1.getMain().getClass(), command2.getMain().getClass()); |
|
||||||
assertEquals(command1.getMain().getClass().getName(), command2.getMain() |
|
||||||
.getClass().getName()); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testOptions() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("handler"); |
|
||||||
String out = ((OptionHandler) command.getMain()).getHelp(); |
|
||||||
assertTrue("Wrong output: " + out, out.contains("--foo")); |
|
||||||
command.run("World", "--foo"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testMixin() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("mixin"); |
|
||||||
GroovyObjectSupport object = (GroovyObjectSupport) command.getMain(); |
|
||||||
String out = (String) object.getProperty("help"); |
|
||||||
assertTrue("Wrong output: " + out, out.contains("--foo")); |
|
||||||
command.run("World", "--foo"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testMixinWithBlock() throws Exception { |
|
||||||
ScriptCommand command = new ScriptCommand("test"); |
|
||||||
GroovyObjectSupport object = (GroovyObjectSupport) command.getMain(); |
|
||||||
String out = (String) object.getProperty("help"); |
|
||||||
System.err.println(out); |
|
||||||
assertTrue("Wrong output: " + out, out.contains("--foo")); |
|
||||||
command.run("World", "--foo", "--bar=2"); |
|
||||||
assertTrue(executed); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* 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.compiler; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.ExpectedException; |
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.sameInstance; |
||||||
|
import static org.junit.Assert.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link ExtendedGroovyClassLoader}. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public class ExtendedGroovyClassLoaderTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
public ExpectedException thrown = ExpectedException.none(); |
||||||
|
|
||||||
|
private ClassLoader contextClassLoader; |
||||||
|
|
||||||
|
private ExtendedGroovyClassLoader defaultScopeGroovyClassLoader; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setup() { |
||||||
|
this.contextClassLoader = Thread.currentThread().getContextClassLoader(); |
||||||
|
this.defaultScopeGroovyClassLoader = new ExtendedGroovyClassLoader( |
||||||
|
GroovyCompilerScope.DEFAULT); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void loadsGroovyFromSameClassLoader() throws Exception { |
||||||
|
Class<?> c1 = this.contextClassLoader.loadClass("groovy.lang.Script"); |
||||||
|
Class<?> c2 = this.defaultScopeGroovyClassLoader.loadClass("groovy.lang.Script"); |
||||||
|
assertThat(c1.getClassLoader(), sameInstance(c2.getClassLoader())); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void filteresNonGroovy() throws Exception { |
||||||
|
this.contextClassLoader.loadClass("org.springframework.util.StringUtils"); |
||||||
|
this.thrown.expect(ClassNotFoundException.class); |
||||||
|
this.defaultScopeGroovyClassLoader |
||||||
|
.loadClass("org.springframework.util.StringUtils"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void loadsJavaTypes() throws Exception { |
||||||
|
this.defaultScopeGroovyClassLoader.loadClass("java.lang.Boolean"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
import org.springframework.util.* |
||||||
|
|
||||||
|
@Component |
||||||
|
public class Test implements CommandLineRunner { |
||||||
|
|
||||||
|
public void run(String... args) throws Exception { |
||||||
|
println "HasClasses-" + ClassUtils.isPresent("missing", null) + "-" + |
||||||
|
ClassUtils.isPresent("org.springframework.boot.SpringApplication", null) + "-" + |
||||||
|
ClassUtils.isPresent(args[0], null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue