Create spring-loader-tools project

Create spring-loader-tools containing utilities that can be used
with both Maven and Gradle plugings.

Refactored existing Maven plugin to use the new project.

Issue: #53129653
This commit is contained in:
Phillip Webb
2013-07-28 21:48:19 -07:00
parent 7ea433dce2
commit 5e4238f38a
35 changed files with 1887 additions and 763 deletions

View File

@@ -0,0 +1,84 @@
/*
* 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.launcher.tools;
import java.io.File;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link Layouts}.
*
* @author Phillip Webb
*/
public class LayoutsTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void jarFile() throws Exception {
assertThat(Layouts.forFile(new File("test.jar")), instanceOf(Layouts.Jar.class));
assertThat(Layouts.forFile(new File("test.JAR")), instanceOf(Layouts.Jar.class));
assertThat(Layouts.forFile(new File("test.jAr")), instanceOf(Layouts.Jar.class));
assertThat(Layouts.forFile(new File("te.st.jar")), instanceOf(Layouts.Jar.class));
}
@Test
public void warFile() throws Exception {
assertThat(Layouts.forFile(new File("test.war")), instanceOf(Layouts.War.class));
assertThat(Layouts.forFile(new File("test.WAR")), instanceOf(Layouts.War.class));
assertThat(Layouts.forFile(new File("test.wAr")), instanceOf(Layouts.War.class));
assertThat(Layouts.forFile(new File("te.st.war")), instanceOf(Layouts.War.class));
}
@Test
public void unknownFile() throws Exception {
this.thrown.equals(IllegalStateException.class);
this.thrown.expectMessage("Unable to deduce layout for 'test.txt'");
Layouts.forFile(new File("test.txt"));
}
@Test
public void jarLayout() throws Exception {
Layout layout = new Layouts.Jar();
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.COMPILE),
equalTo("lib/"));
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.PROVIDED),
equalTo("lib/"));
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.RUNTIME),
equalTo("lib/"));
}
@Test
public void warLayout() throws Exception {
Layout layout = new Layouts.War();
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.COMPILE),
equalTo("WEB-INF/lib/"));
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.PROVIDED),
equalTo("WEB-INF/lib-provided/"));
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.RUNTIME),
equalTo("WEB-INF/lib/"));
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.launcher.tools;
import java.io.IOException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.launcher.tools.sample.ClassWithMainMethod;
import org.springframework.boot.launcher.tools.sample.ClassWithoutMainMethod;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link MainClassFinder}.
*
* @author Phillip Webb
*/
public class MainClassFinderTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private TestJarFile testJarFile;
@Before
public void setup() throws IOException {
this.testJarFile = new TestJarFile(this.temporaryFolder);
}
@Test
public void findMainClassInJar() throws Exception {
this.testJarFile.addClass("B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("A.class", ClassWithoutMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarFile(), "");
assertThat(actual, equalTo("B"));
}
@Test
public void findMainClassInJarSubFolder() throws Exception {
this.testJarFile.addClass("a/b/c/D.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithoutMainMethod.class);
this.testJarFile.addClass("a/b/F.class", ClassWithoutMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarFile(), "");
assertThat(actual, equalTo("a.b.c.D"));
}
@Test
public void usesBreadthFirstJarSearch() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarFile(), "");
assertThat(actual, equalTo("a.B"));
}
@Test
public void findMainClassInJarSubLocation() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithMainMethod.class);
String actual = MainClassFinder
.findMainClass(this.testJarFile.getJarFile(), "a/");
assertThat(actual, equalTo("B"));
}
@Test
public void findMainClassInFolder() throws Exception {
this.testJarFile.addClass("B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("A.class", ClassWithoutMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarSource());
assertThat(actual, equalTo("B"));
}
@Test
public void findMainClassInSubFolder() throws Exception {
this.testJarFile.addClass("a/b/c/D.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithoutMainMethod.class);
this.testJarFile.addClass("a/b/F.class", ClassWithoutMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarSource());
assertThat(actual, equalTo("a.b.c.D"));
}
@Test
public void usesBreadthFirstFolderSearch() throws Exception {
this.testJarFile.addClass("a/B.class", ClassWithMainMethod.class);
this.testJarFile.addClass("a/b/c/E.class", ClassWithMainMethod.class);
String actual = MainClassFinder.findMainClass(this.testJarFile.getJarSource());
assertThat(actual, equalTo("a.B"));
}
}

View File

@@ -0,0 +1,289 @@
/*
* 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.launcher.tools;
import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.launcher.tools.sample.ClassWithMainMethod;
import org.springframework.boot.launcher.tools.sample.ClassWithoutMainMethod;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link Repackager}.
*
* @author Phillip Webb
*/
public class RepackagerTests {
private static final Libraries NO_LIBRARIES = new Libraries() {
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
}
};
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Rule
public ExpectedException thrown = ExpectedException.none();
private TestJarFile testJarFile;
@Before
public void setup() throws IOException {
this.testJarFile = new TestJarFile(this.temporaryFolder);
}
@Test
public void nullSource() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
new Repackager(null);
}
@Test
public void missingSource() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
new Repackager(new File("missing"));
}
@Test
public void directorySource() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
new Repackager(this.temporaryFolder.getRoot());
}
@Test
public void specificMainClass() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithoutMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.setMainClass("a.b.C");
repackager.repackage(NO_LIBRARIES);
Manifest actualManifest = getManifest(file);
assertThat(actualManifest.getMainAttributes().getValue("Main-Class"),
equalTo("org.springframework.boot.loader.JarLauncher"));
assertThat(actualManifest.getMainAttributes().getValue("Start-Class"),
equalTo("a.b.C"));
assertThat(hasLauncherClasses(file), equalTo(true));
}
@Test
public void mainClassFromManifest() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithoutMainMethod.class);
Manifest manifest = new Manifest();
manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
manifest.getMainAttributes().putValue("Main-Class", "a.b.C");
this.testJarFile.addManifest(manifest);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.repackage(NO_LIBRARIES);
Manifest actualManifest = getManifest(file);
assertThat(actualManifest.getMainAttributes().getValue("Main-Class"),
equalTo("org.springframework.boot.loader.JarLauncher"));
assertThat(actualManifest.getMainAttributes().getValue("Start-Class"),
equalTo("a.b.C"));
assertThat(hasLauncherClasses(file), equalTo(true));
}
@Test
public void mainClassFound() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.repackage(NO_LIBRARIES);
Manifest actualManifest = getManifest(file);
assertThat(actualManifest.getMainAttributes().getValue("Main-Class"),
equalTo("org.springframework.boot.loader.JarLauncher"));
assertThat(actualManifest.getMainAttributes().getValue("Start-Class"),
equalTo("a.b.C"));
assertThat(hasLauncherClasses(file), equalTo(true));
}
@Test
public void noMainClass() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithoutMainMethod.class);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Unable to find main class");
new Repackager(this.testJarFile.getFile()).repackage(NO_LIBRARIES);
}
@Test
public void sameSourceAndDestinationWithBackup() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.repackage(NO_LIBRARIES);
assertThat(new File(file.getParent(), file.getName() + ".original").exists(),
equalTo(true));
assertThat(hasLauncherClasses(file), equalTo(true));
}
@Test
public void sameSourceAndDestinationWithoutBackup() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.setBackupSource(false);
repackager.repackage(NO_LIBRARIES);
assertThat(new File(file.getParent(), file.getName() + ".original").exists(),
equalTo(false));
assertThat(hasLauncherClasses(file), equalTo(true));
}
@Test
public void differentDestination() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File source = this.testJarFile.getFile();
File dest = this.temporaryFolder.newFile("different.jar");
Repackager repackager = new Repackager(source);
repackager.repackage(dest, NO_LIBRARIES);
assertThat(new File(source.getParent(), source.getName() + ".original").exists(),
equalTo(false));
assertThat(hasLauncherClasses(source), equalTo(false));
assertThat(hasLauncherClasses(dest), equalTo(true));
}
@Test
public void nullDestination() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
Repackager repackager = new Repackager(this.testJarFile.getFile());
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Invalid destination");
repackager.repackage(null, NO_LIBRARIES);
}
@Test
public void destinationIsDirectory() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
Repackager repackager = new Repackager(this.testJarFile.getFile());
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Invalid destination");
repackager.repackage(this.temporaryFolder.getRoot(), NO_LIBRARIES);
}
@Test
public void overwriteDestination() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
Repackager repackager = new Repackager(this.testJarFile.getFile());
File dest = this.temporaryFolder.newFile("dest.jar");
dest.createNewFile();
repackager.repackage(dest, NO_LIBRARIES);
assertThat(hasLauncherClasses(dest), equalTo(true));
}
@Test
public void nullLibraries() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Libraries must not be null");
repackager.repackage(file, null);
}
@Test
public void libraries() throws Exception {
TestJarFile libJar = new TestJarFile(this.temporaryFolder);
libJar.addClass("a.b.C.class", ClassWithoutMainMethod.class);
final File libJarFile = libJar.getFile();
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
repackager.repackage(new Libraries() {
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
callback.library(libJarFile, LibraryScope.COMPILE);
}
});
assertThat(hasEntry(file, "lib/" + libJarFile.getName()), equalTo(true));
}
@Test
public void customLayout() throws Exception {
TestJarFile libJar = new TestJarFile(this.temporaryFolder);
libJar.addClass("a.b.C.class", ClassWithoutMainMethod.class);
final File libJarFile = libJar.getFile();
this.testJarFile.addClass("a.b.C.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
Layout layout = mock(Layout.class);
final LibraryScope scope = mock(LibraryScope.class);
given(layout.getLauncherClassName()).willReturn("testLauncher");
given(layout.getLibraryDestination(anyString(), eq(scope))).willReturn("test/");
repackager.setLayout(layout);
repackager.repackage(new Libraries() {
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
callback.library(libJarFile, scope);
}
});
assertThat(hasEntry(file, "test/" + libJarFile.getName()), equalTo(true));
assertThat(getManifest(file).getMainAttributes().getValue("Main-Class"),
equalTo("testLauncher"));
}
@Test
public void nullCustomLayout() throws Exception {
this.testJarFile.addClass("a.b.C.class", ClassWithoutMainMethod.class);
Repackager repackager = new Repackager(this.testJarFile.getFile());
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Layout must not be null");
repackager.setLayout(null);
}
private boolean hasLauncherClasses(File file) throws IOException {
return hasEntry(file, "org/springframework/boot/")
&& hasEntry(file, "org/springframework/boot/loader/JarLauncher.class");
}
private boolean hasEntry(File file, String name) throws IOException {
JarFile jarFile = new JarFile(file);
try {
return jarFile.getEntry(name) != null;
}
finally {
jarFile.close();
}
}
private Manifest getManifest(File file) throws IOException {
JarFile jarFile = new JarFile(file);
try {
return jarFile.getManifest();
}
finally {
jarFile.close();
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.launcher.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.junit.rules.TemporaryFolder;
import org.zeroturnaround.zip.ZipUtil;
/**
* @author Phillip Webb
*/
public class TestJarFile {
private final byte[] buffer = new byte[4096];
private TemporaryFolder temporaryFolder;
private File jarSource;
public TestJarFile(TemporaryFolder temporaryFolder) throws IOException {
this.temporaryFolder = temporaryFolder;
this.jarSource = temporaryFolder.newFolder();
}
public void addClass(String filename, Class<?> classToCopy) throws IOException {
String[] paths = filename.split("\\/");
File file = this.jarSource;
for (String path : paths) {
file = new File(file, path);
}
file.getParentFile().mkdirs();
InputStream inputStream = getClass().getResourceAsStream(
"/" + classToCopy.getName().replace(".", "/") + ".class");
OutputStream outputStream = new FileOutputStream(file);
try {
copy(inputStream, outputStream);
}
finally {
outputStream.close();
}
}
public void addManifest(Manifest manifest) throws IOException {
File manifestFile = new File(this.jarSource, "META-INF/MANIFEST.MF");
manifestFile.getParentFile().mkdirs();
OutputStream outputStream = new FileOutputStream(manifestFile);
try {
manifest.write(outputStream);
}
finally {
outputStream.close();
}
}
private void copy(InputStream in, OutputStream out) throws IOException {
int bytesRead = -1;
while ((bytesRead = in.read(this.buffer)) != -1) {
out.write(this.buffer, 0, bytesRead);
}
}
public JarFile getJarFile() throws IOException {
return new JarFile(getFile());
}
public File getJarSource() {
return this.jarSource;
}
public File getFile() throws IOException {
File file = this.temporaryFolder.newFile();
file = new File(file.getParent(), file.getName() + ".jar");
ZipUtil.pack(this.jarSource, file);
return file;
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.launcher.tools.sample;
/**
* Sample class with a main method.
*
* @author Phillip Webb
*/
public class ClassWithMainMethod {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.launcher.tools.sample;
/**
* Sample class without a main method.
*
* @author Phillip Webb
*/
public class ClassWithoutMainMethod {
}

View File

@@ -0,0 +1,11 @@
Manifest-Version: 1.0
Implementation-Title: spring-boot-sample-tomcat
Implementation-Version: 0.5.0.BUILD-SNAPSHOT
Implementation-Vendor-Id: org.springframework.boot
Built-By: pwebb
Build-Jdk: 1.7.0_17
Specification-Title: spring-boot-sample-tomcat
Created-By: Apache Maven 3.0.5
Specification-Version: 0.5.0.BUILD-SNAPSHOT
Archiver-Version: Plexus Archiver

View File

@@ -0,0 +1,7 @@
#Generated by Maven Integration for Eclipse
#Fri Jul 26 13:40:41 PDT 2013
version=0.5.0.BUILD-SNAPSHOT
groupId=org.springframework.boot
m2e.projectName=spring-boot-sample-tomcat
m2e.projectLocation=/Users/pwebb/projects/spring/spring-bootstrap/code/spring-boot-samples/spring-boot-sample-tomcat
artifactId=spring-boot-sample-tomcat

View File

@@ -0,0 +1,39 @@
<?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-samples</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-tomcat</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-up</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-up-tomcat</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1 @@
p.{}