Skip to content

Commit

Permalink
Support custom fat jar layouts
Browse files Browse the repository at this point in the history
Allow support for custom Lyout implementations with both the Maven
and Gradle plugin. Implementations of `LayoutFactory` can now be
specified to allow customization of the layout. In addition a
layout may now implement `CustomLoaderLayout` if it wishes to
write custom loader classes.

See spring-projectsgh-7263
  • Loading branch information
dsyer authored and philwebb committed Nov 29, 2016
1 parent f5b03c8 commit c6c6524
Show file tree
Hide file tree
Showing 14 changed files with 444 additions and 22 deletions.
37 changes: 37 additions & 0 deletions spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,11 @@ The following configuration options are available:
(defaults to a guess based on the archive type). See
<<build-tool-plugins-gradle-configuration-layouts,available layouts for more details>>.

|'layoutFactory`
|A layout factory that can be used if a custom layout is required. Alternative layouts
can be provided by 3rd parties. Layout factories are only used when `layout` is not
specified.

|`requiresUnpack`
|A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from
fat jars in order to run. Items are still packaged into the fat jar, but they will be
Expand Down Expand Up @@ -530,6 +535,38 @@ loader should be included or not. The following layouts are available:



+[[build-tool-plugins-gradle-configuration-custom-repackager]]
+==== 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. Any library which defines one
or more `LayoutFactory` implementations can be added to the build script dependencies
and then the layout factory becomes available in the `springBoot` configuration.
For example:

[source,groovy,indent=0,subs="verbatim,attributes"]
----
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}")
classpath("com.example:custom-layout:1.0.0")
}
}
springBoot {
layoutFactory = new com.example.CustomLayoutFactory()
}
+----
NOTE: 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. Layout factories are only used when no explicit `layout` is
specified.
[[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 @@ -26,6 +26,7 @@

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.Layouts;

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

/**
* The layout factory that will be used when no explicit layout is specified.
* Alternative layouts can be provided by 3rd parties.
*/
LayoutFactory layoutFactory;

/**
* Libraries that must be unpacked from fat jars in order to run. Use Strings in the
* form {@literal groupId:artifactId}.
Expand Down Expand Up @@ -196,6 +203,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
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ private void repackage(File file) {
copy(file, outputFile);
file = outputFile;
}
Repackager repackager = new Repackager(file);
Repackager repackager = new Repackager(file,
this.extension.getLayoutFactory());
repackager.addMainClassTimeoutWarningListener(
new LoggingMainClassTimeoutWarningListener());
setMainClass(repackager);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2012-2016 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.IOException;

/**
* Additional interface that can be implemented by {@link Layout Layouts} that write their
* own loader classes.
*
* @author Phillip Webb
* @since 1.5.0
*/
public interface CustomLoaderLayout {

/**
* Write the required loader classes into the JAR.
* @param writer the writer used to write the classes
* @throws IOException if the classes cannot be written
*/
void writeLoadedClasses(LoaderClassesWriter writer) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2012-2016 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;

/**
* Default implementation of {@link LayoutFactory}.
*
* @author Phillip Webb
* @since 1.5.0
*/
public class DefaultLayoutFactory implements LayoutFactory {

@Override
public Layout getLayout(File source) {
return Layouts.forFile(source);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class JarWriter {
public class JarWriter implements LoaderClassesWriter {

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

Expand Down Expand Up @@ -158,6 +158,7 @@ void writeEntries(JarFile jarFile, EntryTransformer entryTransformer)
* @param inputStream The stream from which the entry's data can be read
* @throws IOException if the write fails
*/
@Override
public void writeEntry(String entryName, InputStream inputStream) throws IOException {
JarEntry entry = new JarEntry(entryName);
writeEntry(entry, new InputStreamEntryWriter(inputStream, true));
Expand Down Expand Up @@ -207,8 +208,20 @@ private long getNestedLibraryTime(File file) {
* Write the required spring-boot-loader classes to the JAR.
* @throws IOException if the classes cannot be written
*/
@Override
public void writeLoaderClasses() throws IOException {
URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR);
writeLoaderClasses(NESTED_LOADER_JAR);
}

/**
* Write the required spring-boot-loader classes to the JAR.
* @param loaderJarResourceName the name of the resource containing the loader classes
* to be written
* @throws IOException if the classes cannot be written
*/
@Override
public void writeLoaderClasses(String loaderJarResourceName) throws IOException {
URL loaderJar = getClass().getClassLoader().getResource(loaderJarResourceName);
JarInputStream inputStream = new JarInputStream(
new BufferedInputStream(loaderJar.openStream()));
JarEntry entry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@

/**
* Strategy interface used to determine the layout for a particular type of archive.
* Layouts may additionally implement {@link CustomLoaderLayout} if they wish to write
* custom loader classes.
*
* @author Phillip Webb
* @see Layouts
* @see RepackagingLayout
* @see CustomLoaderLayout
*/
public interface Layout {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2012-2016 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;

/**
* Factory interface used to create a {@link Layout}.
*
* @author Dave Syer
* @author Phillip Webb
*/
public interface LayoutFactory {

/**
* Return a {@link Layout} for the specified source file.
* @param source the source file
* @return the layout to use for the file
*/
Layout getLayout(File source);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2012-2016 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.IOException;
import java.io.InputStream;

/**
* Writer used by {@link CustomLoaderLayout CustomLoaderLayouts} to write classes into a
* repackaged JAR.
*
* @author Phillip Webb
* @since 1.5.0
*/
public interface LoaderClassesWriter {

/**
* Write the default required spring-boot-loader classes to the JAR.
* @throws IOException if the classes cannot be written
*/
void writeLoaderClasses() throws IOException;

/**
* Write custom required spring-boot-loader classes to the JAR.
* @param loaderJarResourceName the name of the resource containing the loader classes
* to be written
* @throws IOException if the classes cannot be written
*/
void writeLoaderClasses(String loaderJarResourceName) throws IOException;

/**
* Write a single entry to the JAR.
* @param name the name of the entry
* @param inputStream the input stream content
* @throws IOException if the entry cannot be written
*/
void writeEntry(String name, InputStream inputStream) throws IOException;

}
Loading

0 comments on commit c6c6524

Please sign in to comment.