From c1ec5e5c0e9651f8404c3a085ff39731d748b706 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 8 Oct 2013 10:41:35 +0100 Subject: [PATCH] Support @Grab without a version or group Usually, use of @Grab requires you to specify a group, module, and version when identifying a dependency. This can be done in two different ways: @Grab(group='alpha', module='bravo', version='1.0.0') @Grab('alpha:bravo:1.0.0') This commit allows users to only specify a module: the group is inferred and the version is the one dictated by the boot CLI. Both forms are supported: @Grab(module='bravo') @Grab('bravo') Groovy's global AST transformations, which is how Grab is implemented, do not support ordering and we need to augment the AST for the Grab annotation before its processed by the Grab AST transformation. To work around this, reflection is used to get hold of the compile operations in the conversion phase, and a new AST transformation is inserted immediately before the first AST transformation operation. To allow a module's groupId and version to be resolved consistently, META-INF/springcli.properties has been enhanced to include properties for each module that we want to support in the following form: .groudId = .version = and are taken from the Maven project's dependencies and VPP, a Velocity-based pre-processor, is used to automatically generate the enhanced properties file. To prevent pollution of spring-boot-cli's class path with the dependencies that are only required to populate springcli.properties, a separate project, spring-boot-cli-properties, has been created. spring-boot-cli depends upon this now project causing it to, via the shade plug, include the properties file in its jar. Previously DependencyCustomizer allow a dependency to be added by specifying its full coordinates, i.e. a group ID, artifact ID, and version. This commit updates DependencyCustomizer to only require an artifact/module ID. The group ID and version are then resolved using the same mechanism as the enhanced @Grab support. [#56328644] [bs-312] Allow @Grab without version --- pom.xml | 1 + spring-boot-cli-properties/pom.xml | 531 ++++++++++++++++++ .../META-INF/springcli.properties.vpp | 18 + spring-boot-cli/pom.xml | 11 +- spring-boot-cli/samples/simpleGrab.groovy | 24 + .../compiler/ArtifactCoordinatesResolver.java | 46 ++ .../cli/compiler/DependencyCustomizer.java | 63 ++- .../boot/cli/compiler/GroovyCompiler.java | 128 ++++- ...PropertiesArtifactCoordinatesResolver.java | 77 +++ .../JdbcCompilerAutoConfiguration.java | 3 +- .../JmsCompilerAutoConfiguration.java | 32 +- .../RabbitCompilerAutoConfiguration.java | 61 +- .../ReactorCompilerAutoConfiguration.java | 8 +- .../SpringBatchCompilerAutoConfiguration.java | 6 +- .../SpringBootCompilerAutoConfiguration.java | 3 +- ...gIntegrationCompilerAutoConfiguration.java | 13 +- .../SpringMvcCompilerAutoConfiguration.java | 6 +- ...ringSecurityCompilerAutoConfiguration.java | 5 +- ...onManagementCompilerAutoConfiguration.java | 5 +- .../resources/META-INF/springcli.properties | 11 - .../boot/cli/SampleIntegrationTests.java | 31 +- spring-boot-parent/pom.xml | 25 + 22 files changed, 968 insertions(+), 140 deletions(-) create mode 100644 spring-boot-cli-properties/pom.xml create mode 100644 spring-boot-cli-properties/src/main/resources/META-INF/springcli.properties.vpp create mode 100644 spring-boot-cli/samples/simpleGrab.groovy create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ArtifactCoordinatesResolver.java create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/PropertiesArtifactCoordinatesResolver.java delete mode 100644 spring-boot-cli/src/main/resources/META-INF/springcli.properties diff --git a/pom.xml b/pom.xml index 5be17f66d8..1b6adf30c1 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ spring-boot-actuator spring-boot-starters spring-boot-cli + spring-boot-cli-properties spring-boot-integration-tests diff --git a/spring-boot-cli-properties/pom.xml b/spring-boot-cli-properties/pom.xml new file mode 100644 index 0000000000..66578ea7bc --- /dev/null +++ b/spring-boot-cli-properties/pom.xml @@ -0,0 +1,531 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-parent + 0.5.0.BUILD-SNAPSHOT + ../spring-boot-parent + + spring-boot-cli-properties + jar + + ${basedir}/.. + + + + + ${project.groupId} + spring-boot-starter + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-actuator + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-aop + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-batch + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-data-jpa + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-integration + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-jdbc + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-jetty + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-logging + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-security + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-tomcat + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-web + ${project.version} + provided + + + ${project.groupId} + spring-boot-starter-websocket + ${project.version} + provided + + + ch.qos.logback + logback-classic + provided + + + com.fasterxml.jackson.core + jackson-databind + provided + + + com.fasterxml.jackson.core + jackson-core + provided + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + provided + + + commons-dbcp + commons-dbcp + provided + + + commons-httpclient + commons-httpclient + provided + + + javax.servlet + javax.servlet-api + provided + + + javax.servlet + jstl + provided + + + joda-time + joda-time + provided + + + junit + junit + provided + + + log4j + log4j + provided + + + nz.net.ultraq.thymeleaf + thymeleaf-layout-dialect + provided + + + org.apache.activemq + activemq-core + provided + + + org.apache.tomcat.embed + tomcat-embed-core + provided + + + org.apache.tomcat.embed + tomcat-embed-logging-juli + provided + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + org.apache.tomcat + tomcat-jdbc + provided + + + org.apache.tomcat + tomcat-jsp-api + provided + + + org.aspectj + aspectjrt + provided + + + org.aspectj + aspectjweaver + provided + + + org.codehaus.groovy + groovy-templates + provided + + + org.eclipse.jetty + jetty-webapp + provided + + + javax.servlet + org.eclipse.jetty.orbit + + + + + org.eclipse.jetty + jetty-util + provided + + + org.eclipse.jetty + jetty-jsp + provided + + + org.eclipse.jetty + jetty-annotations + provided + + + org.hamcrest + hamcrest-library + provided + + + com.h2database + h2 + provided + + + org.hibernate + hibernate-entitymanager + provided + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + provided + + + org.hibernate + hibernate-validator + provided + + + org.hsqldb + hsqldb + provided + + + org.liquibase + liquibase-core + provided + + + org.projectreactor + reactor-spring + provided + + + org.mockito + mockito-core + provided + + + org.slf4j + jcl-over-slf4j + provided + + + org.slf4j + slf4j-api + provided + + + org.slf4j + jul-to-slf4j + provided + + + org.slf4j + slf4j-log4j12 + provided + + + org.slf4j + slf4j-jdk14 + provided + + + org.springframework + spring-aop + provided + + + org.springframework + spring-beans + provided + + + org.springframework + spring-core + provided + + + org.springframework + spring-context + provided + + + org.springframework + spring-context-support + provided + + + quartz + quartz + + + + + org.springframework + spring-expression + provided + + + org.springframework + spring-jdbc + provided + + + org.springframework + spring-jms + provided + + + org.springframework + spring-messaging + provided + + + org.springframework + spring-orm + provided + + + org.springframework + spring-oxm + provided + + + commons-lang + commons-lang + + + + + org.springframework + spring-test + provided + + + org.springframework + spring-tx + provided + + + org.springframework + spring-websocket + provided + + + org.springframework + spring-web + provided + + + org.springframework + spring-webmvc + provided + + + commons-logging + commons-logging + + + + + org.springframework.batch + spring-batch-core + provided + + + org.springframework.data + spring-data-jpa + provided + + + org.springframework + spring-jdbc + + + org.springframework + spring-orm + + + + + org.springframework.data + spring-data-mongodb + provided + + + org.springframework.integration + spring-integration-core + provided + + + org.springframework.integration + spring-integration-file + provided + + + org.springframework.integration + spring-integration-http + provided + + + org.springframework.integration + spring-integration-ip + provided + + + org.springframework.integration + spring-integration-stream + provided + + + org.springframework.integration + spring-integration-dsl-groovy-core + provided + + + org.springframework.security + spring-security-core + provided + + + org.springframework.security + spring-security-config + provided + + + org.springframework.security + spring-security-web + provided + + + org.springframework.security + spring-security-acl + provided + + + org.thymeleaf + thymeleaf + provided + + + org.thymeleaf + thymeleaf-spring3 + provided + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity3 + provided + + + org.yaml + snakeyaml + provided + + + org.apache.geronimo.specs + geronimo-jms_1.1_spec + provided + + + + + + ${project.build.directory}/generated-resources + + + + + maven-jar-plugin + + + org.apache.maven.plugins + maven-antrun-plugin + + + generate-cli-properties + generate-sources + + + + + + + + + + run + + + + + + + diff --git a/spring-boot-cli-properties/src/main/resources/META-INF/springcli.properties.vpp b/spring-boot-cli-properties/src/main/resources/META-INF/springcli.properties.vpp new file mode 100644 index 0000000000..ff05b44b4e --- /dev/null +++ b/spring-boot-cli-properties/src/main/resources/META-INF/springcli.properties.vpp @@ -0,0 +1,18 @@ +#set( $artifacts = $project.getReference('maven.project').artifacts ) +#foreach( $artifact in $artifacts ) +#if ( $artifact.scope == 'provided' ) +${artifact.artifactId}.version: $artifact.version +${artifact.artifactId}.groupId: $artifact.groupId +#end +#end +groovy.version: $ant.get('groovy.version') +jetty.version: $ant.get('jetty.version') +reactor.version: $ant.get('reactor.version') +spring.version: $ant.get('spring.version') +spring-batch.version: $ant.get('spring-batch.version') +spring-boot.version: $ant.get('project.version') +spring-rabbit.version: $ant.get('spring-rabbit.version') +spring-security.version: $ant.get('spring-security.version') +spring-integration.version: $ant.get('spring-integration.version') +spring-integration-groovydsl.version: $ant.get('spring-integration-groovydsl.version') +tomcat.version: $ant.get('tomcat.version') \ No newline at end of file diff --git a/spring-boot-cli/pom.xml b/spring-boot-cli/pom.xml index 1a0f99dbbd..dd304597b6 100644 --- a/spring-boot-cli/pom.xml +++ b/spring-boot-cli/pom.xml @@ -16,6 +16,11 @@ + + ${project.groupId} + spring-boot-cli-properties + ${project.version} + net.sf.jopt-simple jopt-simple @@ -62,12 +67,6 @@ - - - src/main/resources - true - - maven-surefire-plugin diff --git a/spring-boot-cli/samples/simpleGrab.groovy b/spring-boot-cli/samples/simpleGrab.groovy new file mode 100644 index 0000000000..3528a0d30d --- /dev/null +++ b/spring-boot-cli/samples/simpleGrab.groovy @@ -0,0 +1,24 @@ +package org.test + +@Grab('spring-boot-starter-web') +@Component +class Example implements CommandLineRunner { + + @Autowired + private MyService myService + + void run(String... args) { + print "Hello " + this.myService.sayWorld() + } +} + + +@Service +class MyService { + + String sayWorld() { + return "World!" + } +} + + diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ArtifactCoordinatesResolver.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ArtifactCoordinatesResolver.java new file mode 100644 index 0000000000..4a93150306 --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ArtifactCoordinatesResolver.java @@ -0,0 +1,46 @@ +/* + * 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; + +/** + * A resolver for artifacts' Maven coordinates, allowing a group id or version to be + * obtained from an artifact ID. + * + * @author Andy Wilkinson + */ +public interface ArtifactCoordinatesResolver { + + /** + * Gets the group id of the artifact identified by the given {@code artifactId}. + * Returns {@code null} if the artifact is unknown to the resolver. + * + * @param artifactId The id of the artifact + * + * @return The group id of the artifact + */ + String getGroupId(String artifactId); + + /** + * Gets the version of the artifact identified by the given {@code artifactId}. + * Returns {@code null} if the artifact is unknown to the resolver. + * + * @param artifactId The id of the artifact + * + * @return The version of the artifact + */ + String getVersion(String artifactId); +} diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java index 668d032e84..278a891156 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java @@ -20,14 +20,11 @@ import groovy.grape.Grape; import groovy.lang.Grapes; import groovy.lang.GroovyClassLoader; -import java.net.URL; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Properties; /** * Customizer that allows dependencies to be added during compilation. Delegates to Groovy @@ -43,15 +40,17 @@ public class DependencyCustomizer { private final List> dependencies; - private Properties properties; + private final ArtifactCoordinatesResolver artifactCoordinatesResolver; /** * Create a new {@link DependencyCustomizer} instance. The {@link #call()} method must * be used to actually resolve dependencies. * @param loader */ - public DependencyCustomizer(GroovyClassLoader loader) { + public DependencyCustomizer(GroovyClassLoader loader, + ArtifactCoordinatesResolver artifactCoordinatesResolver) { this.loader = loader; + this.artifactCoordinatesResolver = artifactCoordinatesResolver; this.dependencies = new ArrayList>(); } @@ -61,27 +60,21 @@ public class DependencyCustomizer { */ protected DependencyCustomizer(DependencyCustomizer parent) { this.loader = parent.loader; + this.artifactCoordinatesResolver = parent.artifactCoordinatesResolver; this.dependencies = parent.dependencies; } - public String getProperty(String key) { - return getProperty(key, ""); + public String getVersion(String artifactId) { + return getVersion(artifactId, ""); + } - public String getProperty(String key, String defaultValue) { - if (this.properties == null) { - this.properties = new Properties(); - try { - for (URL url : Collections.list(this.loader - .getResources("META-INF/springcli.properties"))) { - this.properties.load(url.openStream()); - } - } - catch (Exception e) { - // swallow and continue - } + public String getVersion(String artifactId, String defaultVersion) { + String version = this.artifactCoordinatesResolver.getVersion(artifactId); + if (version == null) { + version = defaultVersion; } - return this.properties.getProperty(key, defaultValue); + return version; } /** @@ -216,25 +209,33 @@ public class DependencyCustomizer { } /** - * Add a single dependencies. - * @param group the group ID - * @param module the module ID - * @param version the version + * Add a single dependency and all of its dependencies. The group ID and version of + * the dependency are resolves using the customizer's + * {@link ArtifactCoordinatesResolver}. + * @param module The module ID * @return this {@link DependencyCustomizer} for continued use */ - public DependencyCustomizer add(String group, String module, String version) { - return this.add(group, module, version, true); + public DependencyCustomizer add(String module) { + return this.add(this.artifactCoordinatesResolver.getGroupId(module), module, + this.artifactCoordinatesResolver.getVersion(module), true); } /** - * Add a single dependencies. - * @param group the group ID - * @param module the module ID - * @param version the version + * Add a single dependency and, optionally, all of its dependencies. The group ID and + * version of the dependency are resolves using the customizer's + * {@link ArtifactCoordinatesResolver}. + * @param module The module ID + * @param transitive {@code true} if the transitive dependencies should also be added, + * otherwise {@code false}. * @return this {@link DependencyCustomizer} for continued use */ + public DependencyCustomizer add(String module, boolean transitive) { + return this.add(this.artifactCoordinatesResolver.getGroupId(module), module, + this.artifactCoordinatesResolver.getVersion(module), transitive); + } + @SuppressWarnings("unchecked") - public DependencyCustomizer add(String group, String module, String version, + private DependencyCustomizer add(String group, String module, String version, boolean transitive) { if (canAdd()) { Map dependency = new HashMap(); diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java index 4ed956bbec..27a7ff5a3e 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java @@ -17,17 +17,25 @@ package org.springframework.boot.cli.compiler; import groovy.grape.Grape; +import groovy.lang.Grab; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyClassLoader.ClassCollector; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationUnit; @@ -37,6 +45,8 @@ import org.codehaus.groovy.control.Phases; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.customizers.ImportCustomizer; +import org.codehaus.groovy.transform.ASTTransformation; +import org.codehaus.groovy.transform.ASTTransformationVisitor; /** * Compiler for Groovy source files. Primarily a simple Facade for @@ -61,6 +71,8 @@ public class GroovyCompiler { private ExtendedGroovyClassLoader loader; + private ArtifactCoordinatesResolver artifactCoordinatesResolver; + /** * Create a new {@link GroovyCompiler} instance. * @param configuration the compiler configuration @@ -73,9 +85,12 @@ public class GroovyCompiler { if (configuration.getClasspath().length() > 0) { this.loader.addClasspath(configuration.getClasspath()); } + this.artifactCoordinatesResolver = new PropertiesArtifactCoordinatesResolver( + this.loader); new GrapeEngineCustomizer(Grape.getInstance()).customize(); compilerConfiguration .addCompilationCustomizers(new CompilerAutoConfigureCustomizer()); + } public void addCompilationCustomizers(CompilationCustomizer... customizers) { @@ -115,8 +130,8 @@ public class GroovyCompiler { CompilerConfiguration compilerConfiguration = this.loader.getConfiguration(); - CompilationUnit compilationUnit = new CompilationUnit(compilerConfiguration, - null, this.loader); + final CompilationUnit compilationUnit = new CompilationUnit( + compilerConfiguration, null, this.loader); SourceUnit sourceUnit = new SourceUnit(file[0], compilerConfiguration, this.loader, compilationUnit.getErrorCollector()); ClassCollector collector = this.loader.createCollector(compilationUnit, @@ -124,6 +139,9 @@ public class GroovyCompiler { compilationUnit.setClassgenCallback(collector); compilationUnit.addSources(file); + + addAstTransformations(compilationUnit); + compilationUnit.compile(Phases.CLASS_GENERATION); for (Object loadedClass : collector.getLoadedClasses()) { classes.add((Class) loadedClass); @@ -145,6 +163,43 @@ public class GroovyCompiler { } + @SuppressWarnings("rawtypes") + private void addAstTransformations(final CompilationUnit compilationUnit) { + try { + Field field = CompilationUnit.class.getDeclaredField("phaseOperations"); + field.setAccessible(true); + + LinkedList[] phaseOperations = (LinkedList[]) field.get(compilationUnit); + processConversionOperations(phaseOperations[Phases.CONVERSION]); + } + catch (Exception npe) { + throw new IllegalStateException( + "Phase operations not available from compilation unit"); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void processConversionOperations(LinkedList conversionOperations) { + for (int i = 0; i < conversionOperations.size(); i++) { + Object operation = conversionOperations.get(i); + + if (operation.getClass().getName() + .startsWith(ASTTransformationVisitor.class.getName())) { + conversionOperations.add(i, new CompilationUnit.SourceUnitOperation() { + + private final ASTTransformation transformation = new DefaultDependencyCoordinatesAstTransformation(); + + @Override + public void call(SourceUnit source) throws CompilationFailedException { + this.transformation.visit(new ASTNode[] { source.getAST() }, + source); + } + }); + break; + } + } + } + /** * {@link CompilationCustomizer} to call {@link CompilerAutoConfiguration}s. */ @@ -166,7 +221,8 @@ public class GroovyCompiler { // Early sweep to get dependencies DependencyCustomizer dependencyCustomizer = new DependencyCustomizer( - GroovyCompiler.this.loader); + GroovyCompiler.this.loader, + GroovyCompiler.this.artifactCoordinatesResolver); for (CompilerAutoConfiguration autoConfiguration : customizers) { if (autoConfiguration.matches(classNode)) { if (GroovyCompiler.this.configuration.isGuessDependencies()) { @@ -200,4 +256,70 @@ public class GroovyCompiler { } + private class DefaultDependencyCoordinatesAstTransformation implements + ASTTransformation { + + @Override + public void visit(ASTNode[] nodes, final SourceUnit source) { + for (ASTNode node : nodes) { + if (node instanceof ModuleNode) { + ModuleNode module = (ModuleNode) node; + for (ClassNode classNode : module.getClasses()) { + for (AnnotationNode annotationNode : classNode.getAnnotations()) { + if (isGrabAnnotation(annotationNode)) { + transformGrabAnnotationIfNecessary(annotationNode); + } + } + } + } + } + } + + private boolean isGrabAnnotation(AnnotationNode annotationNode) { + String annotationClassName = annotationNode.getClassNode().getName(); + return annotationClassName.equals(Grab.class.getName()) + || annotationClassName.equals(Grab.class.getSimpleName()); + } + + private void transformGrabAnnotationIfNecessary(AnnotationNode grabAnnotation) { + Expression valueExpression = grabAnnotation.getMember("value"); + if (valueExpression instanceof ConstantExpression) { + ConstantExpression constantExpression = (ConstantExpression) valueExpression; + Object valueObject = constantExpression.getValue(); + if (valueObject instanceof String) { + String value = (String) valueObject; + if (!isConvenienceForm(value)) { + transformGrabAnnotation(grabAnnotation, constantExpression); + } + } + } + } + + private boolean isConvenienceForm(String value) { + return value.contains(":") || value.contains("#"); + } + + private void transformGrabAnnotation(AnnotationNode grabAnnotation, + ConstantExpression moduleExpression) { + + grabAnnotation.setMember("module", moduleExpression); + + String module = (String) moduleExpression.getValue(); + + if (grabAnnotation.getMember("group") == null) { + ConstantExpression groupIdExpression = new ConstantExpression( + GroovyCompiler.this.artifactCoordinatesResolver + .getGroupId(module)); + grabAnnotation.setMember("group", groupIdExpression); + } + if (grabAnnotation.getMember("version") == null) { + ConstantExpression versionExpression = new ConstantExpression( + GroovyCompiler.this.artifactCoordinatesResolver + .getVersion(module)); + grabAnnotation.setMember("version", versionExpression); + } + + } + } + } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/PropertiesArtifactCoordinatesResolver.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/PropertiesArtifactCoordinatesResolver.java new file mode 100644 index 0000000000..63ca10f580 --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/PropertiesArtifactCoordinatesResolver.java @@ -0,0 +1,77 @@ +/* + * 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.lang.GroovyClassLoader; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Properties; + +final class PropertiesArtifactCoordinatesResolver implements ArtifactCoordinatesResolver { + + private final GroovyClassLoader loader; + + private Properties properties = null; + + public PropertiesArtifactCoordinatesResolver(GroovyClassLoader loader) { + this.loader = loader; + } + + @Override + public String getGroupId(String artifactId) { + return getProperty(artifactId + ".groupId"); + } + + @Override + public String getVersion(String artifactId) { + return getProperty(artifactId + ".version"); + } + + private String getProperty(String name) { + if (this.properties == null) { + loadProperties(); + } + return this.properties.getProperty(name); + } + + private void loadProperties() { + Properties properties = new Properties(); + try { + for (URL url : Collections.list(this.loader + .getResources("META-INF/springcli.properties"))) { + InputStream inputStream = url.openStream(); + try { + properties.load(inputStream); + } + catch (IOException ioe) { + // Swallow and continue + } + finally { + inputStream.close(); + } + } + } + catch (IOException e) { + // Swallow and continue + } + this.properties = properties; + } + +} diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JdbcCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JdbcCompilerAutoConfiguration.java index e86442585c..f3bcfd0336 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JdbcCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JdbcCompilerAutoConfiguration.java @@ -38,8 +38,7 @@ public class JdbcCompilerAutoConfiguration extends CompilerAutoConfiguration { @Override public void applyDependencies(DependencyCustomizer dependencies) { dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate") - .add("org.springframework.boot", "spring-boot-starter-jdbc", - dependencies.getProperty("spring-boot.version")); + .add("spring-boot-starter-jdbc"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java index c2182fad48..7ce7ea6ac2 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java @@ -16,6 +16,12 @@ package org.springframework.boot.cli.compiler.autoconfigure; +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; + import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -23,8 +29,6 @@ import org.springframework.boot.cli.compiler.AstUtils; import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; import org.springframework.boot.cli.compiler.DependencyCustomizer; -import java.lang.annotation.*; - /** * {@link CompilerAutoConfiguration} for Spring JMS. * @@ -34,17 +38,15 @@ public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration { @Override public boolean matches(ClassNode classNode) { - // Slightly weird detection algorithm because there is no @Enable annotation for - // Spring JMS - return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging"); + // Slightly weird detection algorithm because there is no @Enable annotation for + // Spring JMS + return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging"); } @Override public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { - dependencies.add("org.springframework", "spring-jms", - dependencies.getProperty("spring.version")).add( - "org.apache.geronimo.specs", "geronimo-jms_1.1_spec", "1.1"); + dependencies.add("spring-jms").add("geronimo-jms_1.1_spec"); } @@ -52,15 +54,15 @@ public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration { public void applyImports(ImportCustomizer imports) throws CompilationFailedException { imports.addStarImports("javax.jms", "org.springframework.jms.core", "org.springframework.jms.listener", - "org.springframework.jms.listener.adapter") - .addImports(EnableJmsMessaging.class.getCanonicalName()); + "org.springframework.jms.listener.adapter").addImports( + EnableJmsMessaging.class.getCanonicalName()); } - @Target(ElementType.TYPE) - @Documented - @Retention(RetentionPolicy.RUNTIME) - public static @interface EnableJmsMessaging { + @Target(ElementType.TYPE) + @Documented + @Retention(RetentionPolicy.RUNTIME) + public static @interface EnableJmsMessaging { - } + } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/RabbitCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/RabbitCompilerAutoConfiguration.java index 8e41eeaf3f..903e366462 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/RabbitCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/RabbitCompilerAutoConfiguration.java @@ -16,6 +16,12 @@ package org.springframework.boot.cli.compiler.autoconfigure; +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; + import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -23,45 +29,42 @@ import org.springframework.boot.cli.compiler.AstUtils; import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; import org.springframework.boot.cli.compiler.DependencyCustomizer; -import java.lang.annotation.*; - /** * {@link CompilerAutoConfiguration} for Spring Rabbit. - * + * * @author Greg Turnquist */ public class RabbitCompilerAutoConfiguration extends CompilerAutoConfiguration { - @Override - public boolean matches(ClassNode classNode) { - // Slightly weird detection algorithm because there is no @Enable annotation for - // Integration - return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbitMessaging"); - } - - @Override - public void applyDependencies(DependencyCustomizer dependencies) - throws CompilationFailedException { - dependencies.add("org.springframework.amqp", "spring-rabbit", - dependencies.getProperty("spring-rabbit.version")); + @Override + public boolean matches(ClassNode classNode) { + // Slightly weird detection algorithm because there is no @Enable annotation for + // Integration + return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbitMessaging"); + } - } + @Override + public void applyDependencies(DependencyCustomizer dependencies) + throws CompilationFailedException { + dependencies.add("spring-rabbit"); - @Override - public void applyImports(ImportCustomizer imports) throws CompilationFailedException { - imports.addStarImports("org.springframework.amqp.rabbit.core", - "org.springframework.amqp.rabbit.connection", - "org.springframework.amqp.rabbit.listener", - "org.springframework.amqp.rabbit.listener.adapter", - "org.springframework.amqp.core").addImports(EnableRabbitMessaging.class.getCanonicalName()); - } + } - @Target(ElementType.TYPE) - @Documented - @Retention(RetentionPolicy.RUNTIME) - public static @interface EnableRabbitMessaging { + @Override + public void applyImports(ImportCustomizer imports) throws CompilationFailedException { + imports.addStarImports("org.springframework.amqp.rabbit.core", + "org.springframework.amqp.rabbit.connection", + "org.springframework.amqp.rabbit.listener", + "org.springframework.amqp.rabbit.listener.adapter", + "org.springframework.amqp.core").addImports( + EnableRabbitMessaging.class.getCanonicalName()); + } - } + @Target(ElementType.TYPE) + @Documented + @Retention(RetentionPolicy.RUNTIME) + public static @interface EnableRabbitMessaging { + } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java index 4b0082b5eb..a1026a774d 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java @@ -36,12 +36,8 @@ public class ReactorCompilerAutoConfiguration extends CompilerAutoConfiguration @Override public void applyDependencies(DependencyCustomizer dependencies) { - dependencies - .ifAnyMissingClasses("org.reactor.Reactor") - .add("org.projectreactor", "reactor-spring", - dependencies.getProperty("reactor.version"), false) - .add("org.projectreactor", "reactor-core", - dependencies.getProperty("reactor.version")); + dependencies.ifAnyMissingClasses("reactor.core.Reactor") + .add("reactor-spring", false).add("reactor-core"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBatchCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBatchCompilerAutoConfiguration.java index 7de29e0e18..098f69b549 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBatchCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBatchCompilerAutoConfiguration.java @@ -38,11 +38,9 @@ public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfigurat @Override public void applyDependencies(DependencyCustomizer dependencies) { dependencies.ifAnyMissingClasses("org.springframework.batch.core.Job").add( - "org.springframework.batch", "spring-batch-core", - dependencies.getProperty("spring-batch.version", "2.2.0.RELEASE")); + "spring-batch-core"); dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate") - .add("org.springframework", "spring-jdbc", - dependencies.getProperty("spring.version")); + .add("spring-jdbc"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBootCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBootCompilerAutoConfiguration.java index 00cf78920e..d23c9b2533 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBootCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringBootCompilerAutoConfiguration.java @@ -39,8 +39,7 @@ public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfigurati @Override public void applyDependencies(DependencyCustomizer dependencies) { dependencies.ifAnyMissingClasses("org.springframework.boot.SpringApplication") - .add("org.springframework.boot", "spring-boot-starter", - dependencies.getProperty("spring-boot.version")); + .add("spring-boot-starter"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringIntegrationCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringIntegrationCompilerAutoConfiguration.java index 832c2a6a2e..23c84bc239 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringIntegrationCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringIntegrationCompilerAutoConfiguration.java @@ -45,16 +45,9 @@ public class SpringIntegrationCompilerAutoConfiguration extends CompilerAutoConf @Override public void applyDependencies(DependencyCustomizer dependencies) { - dependencies - .ifAnyMissingClasses("org.springframework.integration.Message") - .add("org.springframework.integration", "spring-integration-core", - dependencies.getProperty("spring-integration.version")) - .add("org.springframework.integration", - "spring-integration-dsl-groovy-core", - dependencies.getProperty("spring-integration-dsl.version")); - dependencies.ifAnyMissingClasses("groovy.util.XmlParser").add( - "org.codehaus.groovy", "groovy-xml", - dependencies.getProperty("groovy.version")); + dependencies.ifAnyMissingClasses("org.springframework.integration.Message") + .add("spring-integration-core").add("spring-integration-dsl-groovy-core"); + dependencies.ifAnyMissingClasses("groovy.util.XmlParser").add("groovy-xml"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringMvcCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringMvcCompilerAutoConfiguration.java index 7953134d99..56afcd471c 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringMvcCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringMvcCompilerAutoConfiguration.java @@ -46,11 +46,9 @@ public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguratio public void applyDependencies(DependencyCustomizer dependencies) { dependencies .ifAnyMissingClasses("org.springframework.web.servlet.mvc.Controller") - .add("org.springframework.boot", "spring-boot-starter-web", - dependencies.getProperty("spring-boot.version")); + .add("spring-boot-starter-web"); - dependencies.add("org.codehaus.groovy", "groovy-templates", - dependencies.getProperty("groovy.version")); + dependencies.add("groovy-templates"); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityCompilerAutoConfiguration.java index eb1e4e6bd4..d15ddaa577 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/SpringSecurityCompilerAutoConfiguration.java @@ -39,10 +39,7 @@ public class SpringSecurityCompilerAutoConfiguration extends CompilerAutoConfigu dependencies .ifAnyMissingClasses( "org.springframework.security.config.annotation.web.configuration.EnableWebSecurity") - .add("org.springframework.security", "spring-security-config", - dependencies.getProperty("spring-security.version")) - .add("org.springframework.security", "spring-security-web", - dependencies.getProperty("spring-security.version"), false); + .add("spring-security-config").add("spring-security-web", false); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/TransactionManagementCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/TransactionManagementCompilerAutoConfiguration.java index b14f15cf40..ae037fe1db 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/TransactionManagementCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/TransactionManagementCompilerAutoConfiguration.java @@ -41,10 +41,7 @@ public class TransactionManagementCompilerAutoConfiguration extends dependencies .ifAnyMissingClasses( "org.springframework.transaction.annotation.Transactional") - .add("org.springframework", "spring-tx", - dependencies.getProperty("spring.version")) - .add("org.springframework.boot", "spring-boot-starter-aop", - dependencies.getProperty("spring-boot.version")); + .add("spring-tx").add("spring-boot-starter-aop"); } @Override diff --git a/spring-boot-cli/src/main/resources/META-INF/springcli.properties b/spring-boot-cli/src/main/resources/META-INF/springcli.properties deleted file mode 100644 index 10cab5549d..0000000000 --- a/spring-boot-cli/src/main/resources/META-INF/springcli.properties +++ /dev/null @@ -1,11 +0,0 @@ -groovy.version: ${groovy.version} -jetty.version: ${jetty.version} -reactor.version: ${reactor.version} -spring.version: ${spring.version} -spring-batch.version: ${spring-batch.version} -spring-boot.version: ${project.version} -spring-rabbit.version: ${spring-rabbit.version} -spring-security.version: ${spring-security.version} -spring-integration.version: ${spring-integration.version} -spring-integration-groovydsl.version: ${spring-integration-groovydsl.version} -tomcat.version: ${tomcat.version} diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java index 7cdd5aece5..bf1c012e3c 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java @@ -24,7 +24,12 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.ivy.util.FileUtil; -import org.junit.*; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; import org.springframework.boot.OutputCapture; import org.springframework.boot.cli.command.CleanCommand; import org.springframework.boot.cli.command.RunCommand; @@ -89,6 +94,13 @@ public class SampleIntegrationTests { assertTrue("Wrong output: " + output, output.contains("Hello World")); } + @Test + public void simpleGrabSample() throws Exception { + start("samples/simpleGrab.groovy"); + String output = this.outputCapture.getOutputAndRelease(); + assertTrue("Wrong output: " + output, output.contains("Hello World")); + } + @Test public void templateSample() throws Exception { start("samples/template.groovy"); @@ -192,13 +204,14 @@ public class SampleIntegrationTests { FileUtil.forceDelete(new File("activemq-data")); // cleanup ActiveMQ cruft } - @Test - @Ignore // this test requires RabbitMQ to be run, so disable it be default - public void rabbitSample() throws Exception { - start("samples/rabbit.groovy"); - String output = this.outputCapture.getOutputAndRelease(); - assertTrue("Wrong output: " + output, - output.contains("Received Greetings from Spring Boot via RabbitMQ")); - } + @Test + @Ignore + // this test requires RabbitMQ to be run, so disable it be default + public void rabbitSample() throws Exception { + start("samples/rabbit.groovy"); + String output = this.outputCapture.getOutputAndRelease(); + assertTrue("Wrong output: " + output, + output.contains("Received Greetings from Spring Boot via RabbitMQ")); + } } diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml index b4bf2c8c31..bba02f9895 100644 --- a/spring-boot-parent/pom.xml +++ b/spring-boot-parent/pom.xml @@ -201,6 +201,23 @@ maven-plugin-plugin 3.2 + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + org.apache.ant + ant + 1.7.0 + + + foundrylogic.vpp + vpp + 2.2.1 + + + org.codehaus.mojo sonar-maven-plugin @@ -311,6 +328,14 @@ true + + objectstyle + ObjectStyle.org Repository + http://objectstyle.org/maven2/ + + false + +