Skip to content

Support for custom repackaging to change loader classes #7263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,12 @@ The following configuration options are available:
|The name of the custom configuration.

|`layout`
|The type of archive, corresponding to how the dependencies are laid out inside
(defaults to a guess based on the archive type). See
<<build-tool-plugins-gradle-configuration-layouts,available layouts for more details>>.
|The `LayoutType` of archive, corresponding to how the dependencies are laid out inside
(defaults to a guess based on the archive type). See
<<build-tool-plugins-gradle-configuration-layouts,available layouts for more details>>.

|`layoutFactory`
|A factory for the actual `Layout` derived from the `LayoutType`. If the factory is provided then the layout is ignored.

|`requiresUnpack`
|A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from
Expand All @@ -493,7 +496,7 @@ The following configuration options are available:


[[build-tool-plugins-gradle-configuration-layouts]]
==== Available layouts
==== Available built-in layouts

The `layout` attribute configures the format of the archive and whether the bootstrap
loader should be included or not. The following layouts are available:
Expand Down Expand Up @@ -530,6 +533,40 @@ loader should be included or not. The following layouts are available:



[[build-tool-plugins-gradle-configuration-custom-layout]]
==== Using a custom layout
If you have custom requirements for how to arrange the dependencies and loader classes
inside the repackaged jar, you can use a custom layout in addition to the built-in values.
Any library which defines one or more `LayoutFactory` implementations can be added to the
build script dependencies and then the layout type becomes available in the `springBoot`
configuration. For example

[source,groovy,indent=0,subs="verbatim,attributes"]
----
buildscript {
ext {
springBootVersion = '1.5.0.BUILD-SNAPSHOT'
customVersion = '0.0.1.BUILD-SNAPSHOT'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("com.example:custom-layout:${customVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

springBoot {
layoutFactory = new com.example.CustomLayoutFactory()
}
----

If there is only one custom `LayoutFactory` on the build classpath and
it is listed in `META-INF/spring.factories` then it is unnecessary to
explicitly set it in the `springBoot` configuration.

[[build-tool-plugins-understanding-the-gradle-plugin]]
=== Understanding how the Gradle plugin works
When `spring-boot` is applied to your Gradle project a default task named `bootRepackage`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
import java.util.Set;

import groovy.lang.Closure;

import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;

import org.springframework.boot.gradle.buildinfo.BuildInfo;
import org.springframework.boot.loader.tools.Layout;
import org.springframework.boot.loader.tools.LayoutFactory;
import org.springframework.boot.loader.tools.LayoutType;
import org.springframework.boot.loader.tools.Layouts;

/**
Expand Down Expand Up @@ -90,6 +93,11 @@ public class SpringBootPluginExtension {
*/
LayoutType layout;

/**
* The layout factory to use to convert a layout type into an actual layout.
*/
LayoutFactory layoutFactory = Layouts.getDefaultLayoutFactory();

/**
* Libraries that must be unpacked from fat jars in order to run. Use Strings in the
* form {@literal groupId:artifactId}.
Expand Down Expand Up @@ -142,10 +150,21 @@ public SpringBootPluginExtension(Project project) {

/**
* Convenience method for use in a custom task.
* @param file the file to use to guess the layout if necessary
* @return the Layout to use or null if not explicitly set
*/
public Layout convertLayout() {
return (this.layout == null ? null : this.layout.layout);
public Layout convertLayout(File file) {
Layout result;
if (this.layoutFactory != null) {
result = this.layoutFactory.getLayout();
}
else if (this.layout == null) {
result = Layouts.forFile(file);
}
else {
result = Layouts.forType(this.layout);
}
return result;
}

public String getMainClass() {
Expand Down Expand Up @@ -196,6 +215,14 @@ public void setLayout(LayoutType layout) {
this.layout = layout;
}

public LayoutFactory getLayoutFactory() {
return this.layoutFactory;
}

public void setLayoutFactory(LayoutFactory layoutFactory) {
this.layoutFactory = layoutFactory;
}

public Set<String> getRequiresUnpack() {
return this.requiresUnpack;
}
Expand Down Expand Up @@ -276,29 +303,4 @@ public void buildInfo(Closure<?> taskConfigurer) {
}
}

/**
* Layout Types.
*/
enum LayoutType {

JAR(new Layouts.Jar()),

WAR(new Layouts.War()),

ZIP(new Layouts.Expanded()),

DIR(new Layouts.Expanded()),

MODULE(new Layouts.Module()),

NONE(new Layouts.None());

Layout layout;

LayoutType(Layout layout) {
this.layout = layout;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,7 @@ private void repackage(File file) {
}
Repackager repackager = new LoggingRepackager(file);
setMainClass(repackager);
if (this.extension.convertLayout() != null) {
repackager.setLayout(this.extension.convertLayout());
}
repackager.setLayout(this.extension.convertLayout(file));
repackager.setBackupSource(this.extension.isBackupSource());
try {
LaunchScript launchScript = getLaunchScript();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@
*/
public class JarWriter {

private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar";

private static final int BUFFER_SIZE = 32 * 1024;

private final JarOutputStream jarOutput;
Expand Down Expand Up @@ -206,9 +204,20 @@ private long getNestedLibraryTime(File file) {
/**
* Write the required spring-boot-loader classes to the JAR.
* @throws IOException if the classes cannot be written
* @deprecated us {@link #writeLoaderClasses(String)} instead
*/
@Deprecated
public void writeLoaderClasses() throws IOException {
URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR);
writeLoaderClasses(Layouts.DEFAULT_LOADER_JAR);
}

/**
* Write the required spring-boot-loader classes to the JAR.
* @param loaderJarPath the path to the loader jar (in the classpath)
* @throws IOException if the classes cannot be written
*/
public void writeLoaderClasses(String loaderJarPath) throws IOException {
URL loaderJar = getClass().getClassLoader().getResource(loaderJarPath);
JarInputStream inputStream = new JarInputStream(
new BufferedInputStream(loaderJar.openStream()));
JarEntry entry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,25 @@ public interface Layout {
String getLibraryDestination(String libraryName, LibraryScope scope);

/**
* Returns the location of classes within the archive.
* Returns the location of classes within the archive. Empty if the location is the
* root path, otherwise ends with a slash ('/').
* @return the classes location
*/
String getClassesLocation();

/**
* Returns if loader classes should be included to make the archive executable.
* Returns if loader classes should be included to make the archive executable. If
* true, then {@link #getLoaderJarPath()} should point to a valid jar file that
* contains the loader classes.
* @return if the layout is executable
*/
boolean isExecutable();

/**
* Returns the path to a nested jar that contains the loader, and which will be
* unpacked into the root of the repackaged jar.
* @return the path to a nested jar that contains the loader
*/
String getLoaderJarPath();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.loader.tools;

/**
* Strategy for creating instances of {@link Layout}.
*
* @author Dave Syer
*
*/
public interface LayoutFactory {

Layout getLayout();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.loader.tools;

import java.io.File;

/**
* Archive layout types.
*
* @author Dave Syer
*/
public enum LayoutType {

/**
* Jar Layout.
*/
JAR,

/**
* War Layout.
*/
WAR,

/**
* Zip Layout.
*/
ZIP,

/**
* Dir Layout.
*/
DIR,

/**
* Module Layout.
*/
MODULE,

/**
* No Layout.
*/
NONE;


/**
* Return a layout type for the given source file.
* @param file the source file
* @return a {@link Layout}
*/
public static LayoutType forFile(File file) {
if (file == null) {
throw new IllegalArgumentException("File must not be null");
}
if (file.getName().toLowerCase().endsWith(".jar")) {
return JAR;
}
if (file.getName().toLowerCase().endsWith(".war")) {
return WAR;
}
if (file.isDirectory() || file.getName().toLowerCase().endsWith(".zip")) {
return ZIP;
}
throw new IllegalArgumentException("Unable to deduce layout for '" + file + "'");
}

}
Loading