Skip to content

Commit c6c6524

Browse files
Dave Syerphilwebb
Dave Syer
authored andcommitted
Support custom fat jar layouts
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 gh-7263
1 parent f5b03c8 commit c6c6524

File tree

14 files changed

+444
-22
lines changed

14 files changed

+444
-22
lines changed

spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,11 @@ The following configuration options are available:
484484
(defaults to a guess based on the archive type). See
485485
<<build-tool-plugins-gradle-configuration-layouts,available layouts for more details>>.
486486

487+
|'layoutFactory`
488+
|A layout factory that can be used if a custom layout is required. Alternative layouts
489+
can be provided by 3rd parties. Layout factories are only used when `layout` is not
490+
specified.
491+
487492
|`requiresUnpack`
488493
|A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from
489494
fat jars in order to run. Items are still packaged into the fat jar, but they will be
@@ -530,6 +535,38 @@ loader should be included or not. The following layouts are available:
530535

531536

532537

538+
+[[build-tool-plugins-gradle-configuration-custom-repackager]]
539+
+==== Using a custom layout
540+
If you have custom requirements for how to arrange the dependencies and loader classes
541+
inside the repackaged jar, you can use a custom layout. Any library which defines one
542+
or more `LayoutFactory` implementations can be added to the build script dependencies
543+
and then the layout factory becomes available in the `springBoot` configuration.
544+
For example:
545+
546+
[source,groovy,indent=0,subs="verbatim,attributes"]
547+
----
548+
buildscript {
549+
repositories {
550+
mavenCentral()
551+
}
552+
dependencies {
553+
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}")
554+
classpath("com.example:custom-layout:1.0.0")
555+
}
556+
}
557+
558+
springBoot {
559+
layoutFactory = new com.example.CustomLayoutFactory()
560+
}
561+
+----
562+
563+
NOTE: If there is only one custom `LayoutFactory` on the build classpath and it is
564+
listed in `META-INF/spring.factories` then it is unnecessary to explicitly set it in the
565+
`springBoot` configuration. Layout factories are only used when no explicit `layout` is
566+
specified.
567+
568+
569+
533570
[[build-tool-plugins-understanding-the-gradle-plugin]]
534571
=== Understanding how the Gradle plugin works
535572
When `spring-boot` is applied to your Gradle project a default task named `bootRepackage`

spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.springframework.boot.gradle.buildinfo.BuildInfo;
2828
import org.springframework.boot.loader.tools.Layout;
29+
import org.springframework.boot.loader.tools.LayoutFactory;
2930
import org.springframework.boot.loader.tools.Layouts;
3031

3132
/**
@@ -90,6 +91,12 @@ public class SpringBootPluginExtension {
9091
*/
9192
LayoutType layout;
9293

94+
/**
95+
* The layout factory that will be used when no explicit layout is specified.
96+
* Alternative layouts can be provided by 3rd parties.
97+
*/
98+
LayoutFactory layoutFactory;
99+
93100
/**
94101
* Libraries that must be unpacked from fat jars in order to run. Use Strings in the
95102
* form {@literal groupId:artifactId}.
@@ -196,6 +203,14 @@ public void setLayout(LayoutType layout) {
196203
this.layout = layout;
197204
}
198205

206+
public LayoutFactory getLayoutFactory() {
207+
return this.layoutFactory;
208+
}
209+
210+
public void setLayoutFactory(LayoutFactory layoutFactory) {
211+
this.layoutFactory = layoutFactory;
212+
}
213+
199214
public Set<String> getRequiresUnpack() {
200215
return this.requiresUnpack;
201216
}

spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ private void repackage(File file) {
213213
copy(file, outputFile);
214214
file = outputFile;
215215
}
216-
Repackager repackager = new Repackager(file);
216+
Repackager repackager = new Repackager(file,
217+
this.extension.getLayoutFactory());
217218
repackager.addMainClassTimeoutWarningListener(
218219
new LoggingMainClassTimeoutWarningListener());
219220
setMainClass(repackager);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader.tools;
18+
19+
import java.io.IOException;
20+
21+
/**
22+
* Additional interface that can be implemented by {@link Layout Layouts} that write their
23+
* own loader classes.
24+
*
25+
* @author Phillip Webb
26+
* @since 1.5.0
27+
*/
28+
public interface CustomLoaderLayout {
29+
30+
/**
31+
* Write the required loader classes into the JAR.
32+
* @param writer the writer used to write the classes
33+
* @throws IOException if the classes cannot be written
34+
*/
35+
void writeLoadedClasses(LoaderClassesWriter writer) throws IOException;
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader.tools;
18+
19+
import java.io.File;
20+
21+
/**
22+
* Default implementation of {@link LayoutFactory}.
23+
*
24+
* @author Phillip Webb
25+
* @since 1.5.0
26+
*/
27+
public class DefaultLayoutFactory implements LayoutFactory {
28+
29+
@Override
30+
public Layout getLayout(File source) {
31+
return Layouts.forFile(source);
32+
}
33+
34+
}

spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
* @author Phillip Webb
5252
* @author Andy Wilkinson
5353
*/
54-
public class JarWriter {
54+
public class JarWriter implements LoaderClassesWriter {
5555

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

@@ -158,6 +158,7 @@ void writeEntries(JarFile jarFile, EntryTransformer entryTransformer)
158158
* @param inputStream The stream from which the entry's data can be read
159159
* @throws IOException if the write fails
160160
*/
161+
@Override
161162
public void writeEntry(String entryName, InputStream inputStream) throws IOException {
162163
JarEntry entry = new JarEntry(entryName);
163164
writeEntry(entry, new InputStreamEntryWriter(inputStream, true));
@@ -207,8 +208,20 @@ private long getNestedLibraryTime(File file) {
207208
* Write the required spring-boot-loader classes to the JAR.
208209
* @throws IOException if the classes cannot be written
209210
*/
211+
@Override
210212
public void writeLoaderClasses() throws IOException {
211-
URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR);
213+
writeLoaderClasses(NESTED_LOADER_JAR);
214+
}
215+
216+
/**
217+
* Write the required spring-boot-loader classes to the JAR.
218+
* @param loaderJarResourceName the name of the resource containing the loader classes
219+
* to be written
220+
* @throws IOException if the classes cannot be written
221+
*/
222+
@Override
223+
public void writeLoaderClasses(String loaderJarResourceName) throws IOException {
224+
URL loaderJar = getClass().getClassLoader().getResource(loaderJarResourceName);
212225
JarInputStream inputStream = new JarInputStream(
213226
new BufferedInputStream(loaderJar.openStream()));
214227
JarEntry entry;

spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818

1919
/**
2020
* Strategy interface used to determine the layout for a particular type of archive.
21+
* Layouts may additionally implement {@link CustomLoaderLayout} if they wish to write
22+
* custom loader classes.
2123
*
2224
* @author Phillip Webb
2325
* @see Layouts
26+
* @see RepackagingLayout
27+
* @see CustomLoaderLayout
2428
*/
2529
public interface Layout {
2630

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader.tools;
18+
19+
import java.io.File;
20+
21+
/**
22+
* Factory interface used to create a {@link Layout}.
23+
*
24+
* @author Dave Syer
25+
* @author Phillip Webb
26+
*/
27+
public interface LayoutFactory {
28+
29+
/**
30+
* Return a {@link Layout} for the specified source file.
31+
* @param source the source file
32+
* @return the layout to use for the file
33+
*/
34+
Layout getLayout(File source);
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader.tools;
18+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
22+
/**
23+
* Writer used by {@link CustomLoaderLayout CustomLoaderLayouts} to write classes into a
24+
* repackaged JAR.
25+
*
26+
* @author Phillip Webb
27+
* @since 1.5.0
28+
*/
29+
public interface LoaderClassesWriter {
30+
31+
/**
32+
* Write the default required spring-boot-loader classes to the JAR.
33+
* @throws IOException if the classes cannot be written
34+
*/
35+
void writeLoaderClasses() throws IOException;
36+
37+
/**
38+
* Write custom required spring-boot-loader classes to the JAR.
39+
* @param loaderJarResourceName the name of the resource containing the loader classes
40+
* to be written
41+
* @throws IOException if the classes cannot be written
42+
*/
43+
void writeLoaderClasses(String loaderJarResourceName) throws IOException;
44+
45+
/**
46+
* Write a single entry to the JAR.
47+
* @param name the name of the entry
48+
* @param inputStream the input stream content
49+
* @throws IOException if the entry cannot be written
50+
*/
51+
void writeEntry(String name, InputStream inputStream) throws IOException;
52+
53+
}

0 commit comments

Comments
 (0)