Add support for unpacking nested JARs
Update the executable JAR code to automatically unpack any entries which include an entry comment starting `UNPACK:` to the temp folder. The existing Maven and Gradle plugins have been updated with new configuration options and the `spring-boot-tools` project has been updated to write the appropriate entry comment based on a flag passed in via the `Library` class. This support has been added to allow libraries such a JRuby (which assumes that `jruby-complete.jar` is always accessible as file) to work with Spring Boot executable jars. Fixes gh-1070
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?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>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-with-unpack</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<requiresUnpack>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
</requiresUnpack>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Not-Used>Foo</Not-Used>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>4.0.5.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import org.springframework.boot.maven.*;
|
||||
|
||||
File f = new File( basedir, "target/jar-with-unpack-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
new Verify.JarArchiveVerification(f, Verify.SAMPLE_APP) {
|
||||
@Override
|
||||
protected void verifyZipEntries(Verify.ArchiveVerifier verifier) throws Exception {
|
||||
super.verifyZipEntries(verifier)
|
||||
verifier.hasUnpackEntry("lib/spring-core-4.0.5.RELEASE.jar")
|
||||
verifier.hasNonUnpackEntry("lib/spring-context-4.0.5.RELEASE.jar")
|
||||
}
|
||||
}.verify();
|
||||
@@ -17,12 +17,14 @@
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCallback;
|
||||
@@ -47,8 +49,11 @@ public class ArtifactsLibraries implements Libraries {
|
||||
|
||||
private final Set<Artifact> artifacts;
|
||||
|
||||
public ArtifactsLibraries(Set<Artifact> artifacts) {
|
||||
private final Collection<Dependency> unpacks;
|
||||
|
||||
public ArtifactsLibraries(Set<Artifact> artifacts, Collection<Dependency> unpacks) {
|
||||
this.artifacts = artifacts;
|
||||
this.unpacks = unpacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,8 +61,21 @@ public class ArtifactsLibraries implements Libraries {
|
||||
for (Artifact artifact : this.artifacts) {
|
||||
LibraryScope scope = SCOPES.get(artifact.getScope());
|
||||
if (scope != null && artifact.getFile() != null) {
|
||||
callback.library(new Library(artifact.getFile(), scope));
|
||||
callback.library(new Library(artifact.getFile(), scope,
|
||||
isUnpackRequired(artifact)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnpackRequired(Artifact artifact) {
|
||||
if (this.unpacks != null) {
|
||||
for (Dependency unpack : this.unpacks) {
|
||||
if (artifact.getGroupId().equals(unpack.getGroupId())
|
||||
&& artifact.getArtifactId().equals(unpack.getArtifactId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ package org.springframework.boot.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
@@ -108,6 +110,13 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
|
||||
@Parameter
|
||||
private LayoutType layout;
|
||||
|
||||
/**
|
||||
* A list of the libraries that must be unpacked from fat jars in order to run.
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter
|
||||
private List<Dependency> requiresUnpack;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (this.project.getPackaging().equals("pom")) {
|
||||
@@ -144,7 +153,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
|
||||
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
|
||||
getFilters());
|
||||
|
||||
Libraries libraries = new ArtifactsLibraries(artifacts);
|
||||
Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack);
|
||||
try {
|
||||
repackager.repackage(target, libraries);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -62,7 +63,7 @@ public class ArtifactsLibrariesTests {
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.artifacts = Collections.singleton(this.artifact);
|
||||
this.libs = new ArtifactsLibraries(this.artifacts);
|
||||
this.libs = new ArtifactsLibraries(this.artifacts, null);
|
||||
given(this.artifact.getFile()).willReturn(this.file);
|
||||
}
|
||||
|
||||
@@ -75,6 +76,21 @@ public class ArtifactsLibrariesTests {
|
||||
Library library = this.libraryCaptor.getValue();
|
||||
assertThat(library.getFile(), equalTo(this.file));
|
||||
assertThat(library.getScope(), equalTo(LibraryScope.COMPILE));
|
||||
assertThat(library.isUnpackRequired(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbackWithUnpack() throws Exception {
|
||||
given(this.artifact.getGroupId()).willReturn("gid");
|
||||
given(this.artifact.getArtifactId()).willReturn("aid");
|
||||
given(this.artifact.getType()).willReturn("jar");
|
||||
given(this.artifact.getScope()).willReturn("compile");
|
||||
Dependency unpack = new Dependency();
|
||||
unpack.setGroupId("gid");
|
||||
unpack.setArtifactId("aid");
|
||||
this.libs = new ArtifactsLibraries(this.artifacts, Collections.singleton(unpack));
|
||||
this.libs.doWithLibraries(this.callback);
|
||||
verify(this.callback).library(this.libraryCaptor.capture());
|
||||
assertThat(this.libraryCaptor.getValue().isUnpackRequired(), equalTo(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,15 @@ public class Verify {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNonUnpackEntry(String entry) {
|
||||
return !hasUnpackEntry(entry);
|
||||
}
|
||||
|
||||
public boolean hasUnpackEntry(String entry) {
|
||||
String comment = this.content.get(entry).getComment();
|
||||
return comment != null && comment.startsWith("UNPACK:");
|
||||
}
|
||||
|
||||
public boolean hasEntry(String entry) {
|
||||
return this.content.containsKey(entry);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user