Create spring-boot-antlib module

Create a new spring-boot-antlib module which allows Apache Ant users
to easily create executable jars.

Fixes gh-3339
This commit is contained in:
Matt Benson
2015-06-26 17:09:59 -05:00
committed by Phillip Webb
parent 8d948dfa6c
commit ae4559eb4f
13 changed files with 730 additions and 11 deletions

View File

@@ -0,0 +1,93 @@
<?xml version="1.0"?>
<project name="spring-boot-antlib-test" default="antunit" xmlns:au="antlib:org.apache.ant.antunit" xmlns:spring-boot="antlib:org.springframework.boot.ant" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="src.dir" location="src/main/java" />
<property name="resource.dir" location="src/main/resources" />
<property name="target.dir" location="target" />
<property name="classes.dir" location="${target.dir}/classes" />
<target name="setUp">
<ivy:cachepath inline="true" pathid="compile.classpath" organisation="joda-time" module="joda-time" type="jar" revision="2.8.1" />
<mkdir dir="${classes.dir}" />
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="compile.classpath" />
</target>
<target name="tearDown">
<delete dir="${classes.dir}" />
</target>
<target name="test-findmainclass-set-property" depends="setUp">
<spring-boot:findmainclass classesroot="${classes.dir}" property="main-class" />
<au:assertEquals expected="org.test.SampleApplication" actual="${main-class}" />
</target>
<target name="test-findmainclass-log-message" depends="setUp">
<spring-boot:findmainclass classesroot="${classes.dir}" />
<au:assertLogContains text="org.test.SampleApplication" />
</target>
<target name="test-findmainclass-override" depends="setUp">
<spring-boot:findmainclass mainclass="OVERRIDDEN" property="main-class" />
<au:assertEquals expected="OVERRIDDEN" actual="${main-class}" />
</target>
<macrodef name="check-exejar">
<attribute name="jar" />
<sequential>
<echo>Checking @{jar}</echo>
<au:assertFileExists file="@{jar}" />
<javaresource id="foo" name="foo">
<classpath>
<pathelement location="@{jar}" />
</classpath>
</javaresource>
<au:assertRefResourceExists refid="foo" />
<au:assertRefResourceContains refid="foo" value="FOO" />
<java jar="@{jar}" fork="true" />
<au:assertLogContains text="LocalDate" />
</sequential>
</macrodef>
<target name="test-exejar-explicit-start-class" depends="setUp">
<local name="jar" />
<property name="jar" location="${target.dir}/explicit.jar" />
<spring-boot:exejar destfile="${jar}" classes="${classes.dir}" start-class="org.test.SampleApplication">
<resources>
<fileset dir="${resource.dir}" />
</resources>
<lib>
<path refid="compile.classpath" />
</lib>
</spring-boot:exejar>
<check-exejar jar="${jar}" />
</target>
<target name="test-exejar-find-start-class" depends="setUp">
<local name="jar" />
<property name="jar" location="${target.dir}/found.jar" />
<spring-boot:exejar destfile="${jar}" classes="${classes.dir}">
<resources>
<fileset dir="${resource.dir}" />
</resources>
<lib>
<path refid="compile.classpath" />
</lib>
</spring-boot:exejar>
<check-exejar jar="${jar}" />
</target>
<target name="antunit">
<au:antunit>
<au:plainlistener />
<file file="${ant.file}" />
</au:antunit>
</target>
<target name="clean">
<delete dir="${target.dir}" />
</target>
</project>

View File

@@ -0,0 +1,16 @@
<ivysettings>
<settings defaultResolver="chain" />
<resolvers>
<chain name="chain">
<!-- NOTE: You should declare only repositories that you need here -->
<filesystem name="local" local="true" m2compatible="true">
<artifact pattern="${user.home}/.m2/[organisation]/[module]/[revision]/[module]-[revision].[ext]" />
<ivy pattern="${user.home}/.m2/[organisation]/[module]/[revision]/[module]-[revision].pom" />
</filesystem>
<ibiblio name="ibiblio" m2compatible="true" />
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/release" />
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/milestone" />
<ibiblio name="spring-snapshots" m2compatible="true" root="http://repo.spring.io/snapshot" />
</chain>
</resolvers>
</ivysettings>

View File

@@ -0,0 +1,27 @@
/*
* 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.test;
import org.joda.time.LocalDate;
public class SampleApplication {
public static void main(String[] args) {
System.out.println(LocalDate.class.getSimpleName());
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2012-2015 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.ant;
import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.springframework.boot.loader.tools.MainClassFinder;
import org.springframework.util.StringUtils;
/**
* Ant task to find a main class.
*
* @author Matt Benson
* @since 1.3.0
*/
public class FindMainClass extends Task {
private String mainClass;
private File classesRoot;
private String property;
public FindMainClass(Project project) {
setProject(project);
}
@Override
public void execute() throws BuildException {
String mainClass = this.mainClass;
if (!StringUtils.hasText(mainClass)) {
mainClass = findMainClass();
if (!StringUtils.hasText(mainClass)) {
throw new BuildException(
"Could not determine main class given @classesRoot "
+ this.classesRoot);
}
}
handle(mainClass);
}
private String findMainClass() {
if (this.classesRoot == null) {
throw new BuildException(
"one of @mainClass or @classesRoot must be specified");
}
if (!this.classesRoot.exists()) {
throw new BuildException("@classesRoot " + this.classesRoot
+ " does not exist");
}
try {
if (this.classesRoot.isDirectory()) {
return MainClassFinder.findSingleMainClass(this.classesRoot);
}
return MainClassFinder
.findSingleMainClass(new JarFile(this.classesRoot), "/");
}
catch (IOException ex) {
throw new BuildException(ex);
}
}
private void handle(String mainClass) {
if (StringUtils.hasText(this.property)) {
getProject().setProperty(this.property, mainClass);
}
else {
log("Found main class " + mainClass);
}
}
/**
* Set the main class, which will cause the search to be bypassed.
* @param mainClass
*/
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
/**
* Set the root location of classes to be searched.
* @param classesRoot
*/
public void setClassesRoot(File classesRoot) {
this.classesRoot = classesRoot;
}
/**
* Set the property to set (if unset, result will be printed to the log).
* @param property
*/
public void setProperty(String property) {
this.property = property;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2012-2015 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.ant;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.springframework.util.StringUtils;
/**
* Quiet task that establishes a reference to its loader.
*
* @author Matt Benson
* @since 1.3.0
*/
public class ShareAntlibLoader extends Task {
private String refid;
public ShareAntlibLoader(Project project) {
super();
setProject(project);
}
@Override
public void execute() throws BuildException {
if (!StringUtils.hasText(this.refid)) {
throw new BuildException("@refid has no text");
}
getProject().addReference(this.refid, getClass().getClassLoader());
}
public void setRefid(String refid) {
this.refid = refid;
}
}

View File

@@ -0,0 +1,78 @@
<?xml version="1.0"?>
<antlib>
<taskdef name="findmainclass" classname="org.springframework.boot.ant.FindMainClass" />
<!-- unadvertised taskdef to help pull antlib resources from within the antlib.xml file -->
<taskdef name="spring-boot-antlib-share-antlib-loader" classname="org.springframework.boot.ant.ShareAntlibLoader" />
<macrodef name="exejar" description="Create a spring-boot executable jar">
<attribute name="destfile" />
<attribute name="classes" />
<attribute name="start-class" default="" />
<element name="resources" optional="true"
description="includes resource collections specifying additional Java resources" />
<element name="lib" optional="true"
description="includes resource collections containing (jar) dependencies" />
<sequential>
<local name="start-class" />
<findmainclass
xmlns="antlib:org.springframework.boot.ant"
property="start-class"
mainclass="@{start-class}"
classesroot="@{classes}" />
<echo>Using start class ${start-class}</echo>
<spring-boot-antlib-share-antlib-loader
xmlns="antlib:org.springframework.boot.ant"
refid="spring.boot.antlib.loader" />
<local name="spring-boot.version" />
<loadproperties prefix="spring-boot">
<javaresource
name="META-INF/maven/org.springframework.boot/spring-boot-antlib/pom.properties"
loaderref="spring.boot.antlib.loader" />
<filterchain>
<linecontainsregexp>
<regexp pattern="^version=" />
</linecontainsregexp>
</filterchain>
</loadproperties>
<local name="destdir" />
<dirname file="@{destfile}" property="destdir" />
<echo>Using destination directory ${destdir}</echo>
<mkdir dir="${destdir}/dependency" />
<echo>Extracting spring-boot-loader to ${destdir}/dependency</echo>
<copy todir="${destdir}/dependency">
<javaresource name="META-INF/loader/spring-boot-loader.jar"
loaderref="spring.boot.antlib.loader" />
<flattenmapper />
</copy>
<echo>Embedding spring-boot-loader v${spring-boot.version}...</echo>
<jar destfile="@{destfile}" compress="false">
<fileset dir="@{classes}" />
<resources />
<mappedresources>
<lib />
<globmapper from="*" to="lib/*" />
</mappedresources>
<zipfileset src="${destdir}/dependency/spring-boot-loader.jar" />
<manifest>
<attribute name="Main-Class"
value="org.springframework.boot.loader.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" />
</manifest>
</jar>
</sequential>
</macrodef>
</antlib>