diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index fd70bea915..3ea33cb806 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -616,6 +616,10 @@
maven-install-plugin
2.5.1
+
+ maven-help-plugin
+ 2.2
+
maven-jar-plugin
2.4
diff --git a/spring-boot-tools/pom.xml b/spring-boot-tools/pom.xml
index 4eaa084c17..d5bbe494fe 100644
--- a/spring-boot-tools/pom.xml
+++ b/spring-boot-tools/pom.xml
@@ -14,6 +14,7 @@
${basedir}/..
+ spring-boot-dependency-tools
spring-boot-loader
spring-boot-loader-tools
spring-boot-maven-plugin
diff --git a/spring-boot-tools/spring-boot-dependency-tools/README.md b/spring-boot-tools/spring-boot-dependency-tools/README.md
new file mode 100644
index 0000000000..c1f17ba629
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/README.md
@@ -0,0 +1,4 @@
+# Spring Boot - Dependency Tools
+The Spring Boot Dependency Tools module provides support utilities to help when resolving
+'blessed' Spring Boot dependencies. It basically provides programmatic access to the
+managed dependencies section of `spring-boot-dependencies/pom.xml`
diff --git a/spring-boot-tools/spring-boot-dependency-tools/pom.xml b/spring-boot-tools/spring-boot-dependency-tools/pom.xml
new file mode 100644
index 0000000000..ba14fa4bc0
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/pom.xml
@@ -0,0 +1,115 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-tools
+ 1.0.0.BUILD-SNAPSHOT
+
+ spring-boot-dependency-tools
+ jar
+
+ ${basedir}/../..
+ ${project.build.directory}/generated-resources/org/springframework/boot/dependency/tools
+
+
+
+
+ ${project.build.directory}/generated-resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ copy-dependencies-pom
+ generate-resources
+
+ run
+
+
+
+
+
+
+
+
+
+
+ maven-help-plugin
+
+
+ generate-effective-dependencies-pom
+ generate-resources
+
+ effective-pom
+
+
+
+
+
+
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+
+
+ maven-help-plugin
+
+
+ [2.2,)
+
+
+ effective-pom
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+
+
+ maven-antrun-plugin
+
+
+ [1.7,)
+
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Assert.java b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Assert.java
new file mode 100644
index 0000000000..0b11738154
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Assert.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012-2014 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.dependency.tools;
+
+/**
+ * Simple subset of the Spring Assert utility.
+ *
+ * @author Phillip Webb
+ */
+class Assert {
+
+ public static void notNull(Object object, String message) {
+ if (object == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Dependency.java b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Dependency.java
new file mode 100644
index 0000000000..525e544068
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/Dependency.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2012-2014 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.dependency.tools;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A single managed dependency.
+ *
+ * @author Phillip Webb
+ * @see ManagedDependencies
+ */
+public final class Dependency {
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ private final String version;
+
+ private final List exclusions;
+
+ /**
+ * Create a new {@link Dependency} instance.
+ * @param groupId the group ID
+ * @param artifactId the artifact ID
+ * @param version the version
+ */
+ public Dependency(String groupId, String artifactId, String version) {
+ this(groupId, artifactId, version, Collections. emptyList());
+ }
+
+ /**
+ * Create a new {@link Dependency} instance.
+ * @param groupId the group ID
+ * @param artifactId the artifact ID
+ * @param version the version
+ * @param exclusions the exclusions
+ */
+ public Dependency(String groupId, String artifactId, String version,
+ List exclusions) {
+ Assert.notNull(groupId, "GroupId must not be null");
+ Assert.notNull(artifactId, "ArtifactId must not be null");
+ Assert.notNull(version, "Version must not be null");
+ Assert.notNull(exclusions, "Exclusions must not be null");
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.version = version;
+ this.exclusions = Collections.unmodifiableList(exclusions);
+ }
+
+ /**
+ * Return the dependency group id.
+ */
+ public String getGroupId() {
+ return this.groupId;
+ }
+
+ /**
+ * Return the dependency artifact id.
+ */
+ public String getArtifactId() {
+ return this.artifactId;
+ }
+
+ /**
+ * Return the dependency version.
+ */
+ public String getVersion() {
+ return this.version;
+ }
+
+ /**
+ * Return the dependency exclusions.
+ */
+ public List getExclusions() {
+ return this.exclusions;
+ }
+
+ @Override
+ public String toString() {
+ return this.groupId + ":" + this.artifactId + ":" + this.version;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + this.groupId.hashCode();
+ result = prime * result + this.artifactId.hashCode();
+ result = prime * result + this.version.hashCode();
+ result = prime * result + this.exclusions.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() == obj.getClass()) {
+ Dependency other = (Dependency) obj;
+ boolean result = true;
+ result &= this.groupId.equals(other.groupId);
+ result &= this.artifactId.equals(other.artifactId);
+ result &= this.version.equals(other.version);
+ result &= this.exclusions.equals(other.exclusions);
+ return result;
+ }
+ return false;
+ }
+
+ static Dependency fromDependenciesXml(Element element) throws Exception {
+ String groupId = getTextContent(element, "groupId");
+ String artifactId = getTextContent(element, "artifactId");
+ String version = getTextContent(element, "version");
+ List exclusions = Exclusion.fromExclusionsXml(element
+ .getElementsByTagName("exclusions"));
+ return new Dependency(groupId, artifactId, version, exclusions);
+ }
+
+ private static String getTextContent(Element element, String tagName) {
+ return element.getElementsByTagName(tagName).item(0).getTextContent();
+ }
+
+ /**
+ * A dependency exclusion.
+ */
+ public final static class Exclusion {
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ private Exclusion(String groupId, String artifactId) {
+ Assert.notNull(groupId, "GroupId must not be null");
+ Assert.notNull(groupId, "ArtifactId must not be null");
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ /**
+ * Return the exclusion artifact id.
+ */
+ public String getArtifactId() {
+ return this.artifactId;
+ }
+
+ /**
+ * Return the exclusion group id.
+ */
+ public String getGroupId() {
+ return this.groupId;
+ }
+
+ @Override
+ public String toString() {
+ return this.groupId + ":" + this.artifactId;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.groupId.hashCode() * 31 + this.artifactId.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() == obj.getClass()) {
+ Exclusion other = (Exclusion) obj;
+ boolean result = true;
+ result &= this.groupId.equals(other.groupId);
+ result &= this.artifactId.equals(other.artifactId);
+ return result;
+ }
+ return false;
+ }
+
+ private static List fromExclusionsXml(NodeList exclusion) {
+ if (exclusion == null || exclusion.getLength() == 0) {
+ return Collections.emptyList();
+ }
+ return fromExclusionsXml(exclusion.item(0));
+ }
+
+ private static List fromExclusionsXml(Node item) {
+ List exclusions = new ArrayList();
+ NodeList children = item.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child instanceof Element) {
+ exclusions.add(fromExclusionXml((Element) child));
+ }
+ }
+ return exclusions;
+ }
+
+ private static Exclusion fromExclusionXml(Element element) {
+ String groupId = getTextContent(element, "groupId");
+ String artifactId = getTextContent(element, "artifactId");
+ return new Exclusion(groupId, artifactId);
+ }
+
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/ManagedDependencies.java b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/ManagedDependencies.java
new file mode 100644
index 0000000000..c44faa15ea
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/ManagedDependencies.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2012-2014 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.dependency.tools;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Provides access to the managed dependencies declared in
+ * {@literal spring-boot-dependencies}.
+ *
+ * @author Phillip Webb
+ * @see Dependency
+ */
+public class ManagedDependencies implements Iterable {
+
+ private static ManagedDependencies instance;
+
+ private final String version;
+
+ private final Map byArtifactAndGroupId;
+
+ private final Map byArtifactId;
+
+ ManagedDependencies(String dependenciesPomResource, String effectivePomResource) {
+ try {
+ Document dependenciesPomDocument = readDocument(dependenciesPomResource);
+ this.version = dependenciesPomDocument.getElementsByTagName("version")
+ .item(0).getTextContent();
+
+ // Parse all dependencies from the effective POM (with resolved properties)
+ Document effectivePomDocument = readDocument(effectivePomResource);
+ Map all = new HashMap();
+ for (Dependency dependency : readDependencies(effectivePomDocument)) {
+ all.put(new ArtifactAndGroupId(dependency), dependency);
+ }
+
+ // But only add those from the dependencies POM
+ this.byArtifactAndGroupId = new LinkedHashMap();
+ this.byArtifactId = new LinkedHashMap();
+ for (Dependency dependency : readDependencies(dependenciesPomDocument)) {
+ ArtifactAndGroupId artifactAndGroupId = new ArtifactAndGroupId(dependency);
+ Dependency effectiveDependency = all.get(artifactAndGroupId);
+ if (effectiveDependency != null) {
+ this.byArtifactAndGroupId
+ .put(artifactAndGroupId, effectiveDependency);
+ this.byArtifactId.put(effectiveDependency.getArtifactId(),
+ effectiveDependency);
+ }
+ }
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private Document readDocument(String resource) throws Exception {
+ InputStream stream = getClass().getResourceAsStream(resource);
+ if (stream == null) {
+ throw new IllegalStateException("Unable to open resource " + resource);
+ }
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder();
+ Document document = documentBuilder.parse(stream);
+ document.getDocumentElement().normalize();
+ return document;
+ }
+
+ private List readDependencies(Document document) throws Exception {
+ Element element = (Element) document.getElementsByTagName("project").item(0);
+ element = (Element) element.getElementsByTagName("dependencyManagement").item(0);
+ element = (Element) element.getElementsByTagName("dependencies").item(0);
+ NodeList nodes = element.getChildNodes();
+ List dependencies = new ArrayList();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (node instanceof Element) {
+ dependencies.add(Dependency.fromDependenciesXml((Element) node));
+ }
+ }
+ return dependencies;
+ }
+
+ /**
+ * Return the 'spring-boot-dependencies' POM version.
+ */
+ public String getVersion() {
+ return this.version;
+ }
+
+ /**
+ * Find a single dependency for the given group and artifact IDs.
+ * @param groupId the group ID
+ * @param artifactId the artifact ID
+ * @return a {@link Dependency} or {@code null}
+ */
+ public Dependency find(String groupId, String artifactId) {
+ return this.byArtifactAndGroupId.get(new ArtifactAndGroupId(groupId, artifactId));
+ }
+
+ /**
+ * Find a single dependency for the artifact IDs.
+ * @param artifactId the artifact ID
+ * @return a {@link Dependency} or {@code null}
+ */
+ public Dependency find(String artifactId) {
+ return this.byArtifactId.get(artifactId);
+ }
+
+ /**
+ * Provide an {@link Iterator} over all managed {@link Dependency Dependencies}.
+ */
+ @Override
+ public Iterator iterator() {
+ return this.byArtifactAndGroupId.values().iterator();
+ }
+
+ /**
+ * @return The Spring Boot managed dependencies.
+ */
+ public static ManagedDependencies get() {
+ if (instance == null) {
+ return new ManagedDependencies("dependencies-pom.xml", "effective-pom.xml");
+ }
+ return instance;
+ }
+
+ /**
+ * Simple holder for an artifact+group ID.
+ */
+ private static class ArtifactAndGroupId {
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ public ArtifactAndGroupId(Dependency dependency) {
+ this(dependency.getGroupId(), dependency.getArtifactId());
+ }
+
+ public ArtifactAndGroupId(String groupId, String artifactId) {
+ Assert.notNull(groupId, "GroupId must not be null");
+ Assert.notNull(artifactId, "ArtifactId must not be null");
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.groupId.hashCode() * 31 + this.artifactId.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() == obj.getClass()) {
+ ArtifactAndGroupId other = (ArtifactAndGroupId) obj;
+ boolean result = true;
+ result &= this.groupId.equals(other.groupId);
+ result &= this.artifactId.equals(other.artifactId);
+ return result;
+ }
+ return false;
+ }
+
+ }
+}
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/package-info.java b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/package-info.java
new file mode 100644
index 0000000000..9271ab60ae
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2012-2014 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.
+ */
+
+/**
+ * Utilities for working with the managed dependencies declared in the
+ * {@literal spring-boot-dependencies} project.
+ *
+ * @see org.springframework.boot.dependency.tools.ManagedDependencies
+ */
+package org.springframework.boot.dependency.tools;
+
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/test/java/org/springframework/boot/dependency/tools/ManagedDependenciesTests.java b/spring-boot-tools/spring-boot-dependency-tools/src/test/java/org/springframework/boot/dependency/tools/ManagedDependenciesTests.java
new file mode 100644
index 0000000000..33cba815ad
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/test/java/org/springframework/boot/dependency/tools/ManagedDependenciesTests.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012-2014 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.dependency.tools;
+
+import java.util.Iterator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests for {@link ManagedDependencies}.
+ *
+ * @author Phillip Webb
+ */
+public class ManagedDependenciesTests {
+
+ private ManagedDependencies dependencies;
+
+ @Before
+ public void setup() {
+ this.dependencies = new ManagedDependencies("test-dependencies-pom.xml",
+ "test-effective-pom.xml");
+ }
+
+ @Test
+ public void version() throws Exception {
+ assertThat(this.dependencies.getVersion(), equalTo("1.0.0.BUILD-SNAPSHOT"));
+ }
+
+ @Test
+ public void iterate() throws Exception {
+ Iterator iterator = this.dependencies.iterator();
+ assertThat(iterator.next().toString(), equalTo("org.sample:sample01:1.0.0"));
+ assertThat(iterator.next().toString(), equalTo("org.sample:sample02:1.0.0"));
+ assertThat(iterator.hasNext(), equalTo(false));
+ }
+
+ @Test
+ public void findByArtifactAndGroupId() throws Exception {
+ assertThat(this.dependencies.find("org.sample", "sample02").toString(),
+ equalTo("org.sample:sample02:1.0.0"));
+ }
+
+ @Test
+ public void findByArtifactAndGroupIdMissing() throws Exception {
+ assertThat(this.dependencies.find("org.sample", "missing"), nullValue());
+ }
+
+ @Test
+ public void findByArtifactAndGroupIdOnlyInEffectivePom() throws Exception {
+ assertThat(this.dependencies.find("org.extra", "extra01"), nullValue());
+ }
+
+ @Test
+ public void findByArtifactId() throws Exception {
+ assertThat(this.dependencies.find("sample02").toString(),
+ equalTo("org.sample:sample02:1.0.0"));
+ }
+
+ @Test
+ public void findByArtifactIdMissing() throws Exception {
+ assertThat(this.dependencies.find("missing"), nullValue());
+ }
+
+ @Test
+ public void exludes() throws Exception {
+ Dependency dependency = this.dependencies.find("org.sample", "sample01");
+ assertThat(dependency.getExclusions().toString(),
+ equalTo("[org.exclude:exclude01]"));
+ }
+
+ @Test
+ public void get() throws Exception {
+ ManagedDependencies dependencies = ManagedDependencies.get();
+ assertThat(dependencies.iterator().hasNext(), equalTo(true));
+ assertThat(dependencies.find("org.springframework", "spring-core"),
+ notNullValue());
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-dependencies-pom.xml b/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-dependencies-pom.xml
new file mode 100644
index 0000000000..2bf7cf04b2
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-dependencies-pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+ 1.0.0.BUILD-SNAPSHOT
+
+ 1.0.0
+
+
+ 3.0.0
+
+
+
+
+ org.sample
+ sample01
+ ${sample.version}
+
+
+ org.exclude
+ exclude01
+
+
+
+
+ org.sample
+ sample02
+ ${sample.version}
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-effective-pom.xml b/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-effective-pom.xml
new file mode 100644
index 0000000000..f24b636687
--- /dev/null
+++ b/spring-boot-tools/spring-boot-dependency-tools/src/test/resources/org/springframework/boot/dependency/tools/test-effective-pom.xml
@@ -0,0 +1,34 @@
+
+
+ 4.0.0
+ 1.0.0.BUILD-SNAPSHOT
+
+ 1.0.0
+
+
+
+
+ org.sample
+ sample01
+ 1.0.0
+
+
+ org.exclude
+ exclude01
+
+
+
+
+ org.sample
+ sample02
+ 1.0.0
+
+
+ org.extra
+ extra01
+ 2.0.0
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/README.md b/spring-boot-tools/spring-boot-gradle-plugin/README.md
index b7ac04897a..c6b547728b 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/README.md
+++ b/spring-boot-tools/spring-boot-gradle-plugin/README.md
@@ -1,6 +1,7 @@
# Spring Boot - Gradle Plugin
The Spring Boot Gradle Plugin provides Spring Boot support in Gradle, allowing you to
-package executable jar or war archives.
+package executable jar or war archives, run Spring Boot applications and remove version
+information from your `build.gradle` file.
## Including the plugin
To use the Spring Boot Gradle Plugin simply include a `buildscript` dependency and apply
@@ -27,6 +28,23 @@ buildscript {
}
```
+## Declaring dependencies without versions
+The `spring-boot` plugin will register a custom Gradle `ResolutionStrategy` with your
+build that allows you to omit version numbers when declaring dependencies to known
+artifacts. All artifacts with a `org.springframework.boot` group ID, and any of the
+artifacts declared in the `managementDependencies` section of the `spring-dependencies`
+POM can have their version number resolved automatically.
+
+Simply declare dependencies in the usual way, but leave the version number empty:
+
+```groovy
+dependencies {
+ compile("org.springframework.boot:spring-boot-starter-web")
+ compile("org.thymeleaf:thymeleaf-spring4")
+ compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
+}
+```
+
## Packaging executable jar and war files
Once the `spring-boot` plugin has been applied to your project it will automatically
attempt to rewrite archives to make them executable using the `bootRepackage` task. You
@@ -45,7 +63,6 @@ $ java -jar build/libs/mymodule-0.0.1-SNAPSHOT.jar
```
### Running a Project in Place
-
To run a project in place without building a jar first you can use the "bootRun" task:
```
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml
index e35a486081..ea9025f090 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml
+++ b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml
@@ -18,6 +18,11 @@
spring-boot-loader-tools
${project.version}
+
+ ${project.groupId}
+ spring-boot-dependency-tools
+ ${project.version}
+
org.codehaus.groovy
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java
index a872cbe3ef..8c90e4034a 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java
@@ -16,8 +16,10 @@
package org.springframework.boot.gradle;
+import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.BasePlugin;
@@ -26,8 +28,8 @@ import org.springframework.boot.gradle.task.Repackage;
import org.springframework.boot.gradle.task.RunApp;
/**
- * Gradle 'Spring Boot' {@link Plugin}. Provides 2 tasks (bootRepackge and bootRun).
- *
+ * Gradle 'Spring Boot' {@link Plugin}.
+ *
* @author Phillip Webb
* @author Dave Syer
*/
@@ -44,19 +46,31 @@ public class SpringBootPlugin implements Plugin {
project.getPlugins().apply(ApplicationPlugin.class);
project.getExtensions().create("springBoot", SpringBootPluginExtension.class);
- // register BootRepackage so that we can use
- // task foo(type: BootRepackage) {}
- project.getExtensions().getExtraProperties().set("BootRepackage", Repackage.class);
+ applyRepackage(project);
+ applyRun(project);
+ applyResolutionStrategy(project);
+ }
+
+ private void applyRepackage(Project project) {
Repackage packageTask = addRepackageTask(project);
ensureTaskRunsOnAssembly(project, packageTask);
+ }
+
+ private void applyRun(Project project) {
addRunAppTask(project);
+ // register BootRepackage so that we can use task foo(type: BootRepackage) {}
+ project.getExtensions().getExtraProperties().set("BootRepackage", Repackage.class);
}
- private void addRunAppTask(Project project) {
- RunApp runJarTask = project.getTasks().create(RUN_APP_TASK_NAME, RunApp.class);
- runJarTask.setDescription("Run the project with support for auto-detecting main class and reloading static resources");
- runJarTask.setGroup("Execution");
- runJarTask.dependsOn("assemble");
+ private void applyResolutionStrategy(Project project) {
+ project.getConfigurations().all(new Action() {
+
+ @Override
+ public void execute(Configuration configuration) {
+ SpringBootResolutionStrategy.apply(configuration.getResolutionStrategy());
+ }
+
+ });
}
private Repackage addRepackageTask(Project project) {
@@ -71,6 +85,13 @@ public class SpringBootPlugin implements Plugin {
return packageTask;
}
+ private void addRunAppTask(Project project) {
+ RunApp runJarTask = project.getTasks().create(RUN_APP_TASK_NAME, RunApp.class);
+ runJarTask.setDescription("Run the project with support for auto-detecting main class and reloading static resources");
+ runJarTask.setGroup("Execution");
+ runJarTask.dependsOn("assemble");
+ }
+
private void ensureTaskRunsOnAssembly(Project project, Repackage task) {
project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME).dependsOn(task);
}
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy
index 938edcb663..d69c9b5dad 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy
@@ -39,8 +39,19 @@ import org.springframework.boot.loader.tools.Layouts
public class SpringBootPluginExtension {
static enum LayoutType {
- JAR(new Layouts.Jar()), WAR(new Layouts.War()), ZIP(new Layouts.Expanded()), DIR(new Layouts.Expanded()), NONE(new Layouts.None());
+
+ JAR(new Layouts.Jar()),
+
+ WAR(new Layouts.War()),
+
+ ZIP(new Layouts.Expanded()),
+
+ DIR(new Layouts.Expanded()),
+
+ NONE(new Layouts.None());
+
Layout layout;
+
private LayoutType(Layout layout) {
this.layout = layout;
}
@@ -85,6 +96,6 @@ public class SpringBootPluginExtension {
* @return the Layout to use or null if not explicitly set
*/
Layout convertLayout() {
- layout==null ? null : layout.layout
+ (layout == null ? null : layout.layout)
}
}
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootResolutionStrategy.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootResolutionStrategy.java
new file mode 100644
index 0000000000..c14cb65414
--- /dev/null
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootResolutionStrategy.java
@@ -0,0 +1,51 @@
+
+package org.springframework.boot.gradle;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.DependencyResolveDetails;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.ResolutionStrategy;
+import org.springframework.boot.dependency.tools.Dependency;
+import org.springframework.boot.dependency.tools.ManagedDependencies;
+
+/**
+ * A resolution strategy to resolve missing version numbers using the
+ * 'spring-boot-dependencies' POM.
+ *
+ * @author Phillip Webb
+ */
+public class SpringBootResolutionStrategy {
+
+ private static final String SPRING_BOOT_GROUP = "org.springframework.boot";
+
+ public static void apply(ResolutionStrategy resolutionStrategy) {
+ resolutionStrategy.eachDependency(new Action() {
+
+ @Override
+ public void execute(DependencyResolveDetails resolveDetails) {
+ String version = resolveDetails.getTarget().getVersion();
+ if (version == null || version.trim().length() == 0) {
+ resolve(resolveDetails);
+ }
+ }
+
+ });
+ }
+
+ protected static void resolve(DependencyResolveDetails resolveDetails) {
+
+ ManagedDependencies dependencies = ManagedDependencies.get();
+ ModuleVersionSelector target = resolveDetails.getTarget();
+
+ if (SPRING_BOOT_GROUP.equals(target.getGroup())) {
+ resolveDetails.useVersion(dependencies.getVersion());
+ return;
+ }
+
+ Dependency dependency = dependencies.find(target.getGroup(), target.getName());
+ if (dependency != null) {
+ resolveDetails.useVersion(dependency.getVersion());
+ }
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java
index dd65847684..1068e8de88 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java
@@ -36,7 +36,7 @@ import org.springframework.boot.loader.tools.MainClassFinder;
/**
* Run the project from Gradle.
- *
+ *
* @author Dave Syer
*/
public class RunApp extends DefaultTask {
@@ -57,6 +57,7 @@ public class RunApp extends DefaultTask {
main = set;
}
};
+
});
final Set allResources = new LinkedHashSet();