Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
ca202ad5
Commit
ca202ad5
authored
Mar 16, 2020
by
Andy Wilkinson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support Maven's outputTimestamp when repackaging jars and wars
Closes gh-20176
parent
df8c25e2
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
319 additions
and
6 deletions
+319
-6
JarWriter.java
...java/org/springframework/boot/loader/tools/JarWriter.java
+24
-1
Repackager.java
...ava/org/springframework/boot/loader/tools/Repackager.java
+24
-4
build.gradle
...t/spring-boot-tools/spring-boot-maven-plugin/build.gradle
+1
-0
JarIntegrationTests.java
...a/org/springframework/boot/maven/JarIntegrationTests.java
+39
-0
WarIntegrationTests.java
...a/org/springframework/boot/maven/WarIntegrationTests.java
+39
-0
pom.xml
...-plugin/src/intTest/projects/jar-output-timestamp/pom.xml
+58
-0
SampleApplication.java
...t-timestamp/src/main/java/org/test/SampleApplication.java
+24
-0
pom.xml
...-plugin/src/intTest/projects/war-output-timestamp/pom.xml
+56
-0
SampleApplication.java
...t-timestamp/src/main/java/org/test/SampleApplication.java
+24
-0
index.html
.../projects/war-output-timestamp/src/main/webapp/index.html
+1
-0
RepackageMojo.java
...in/java/org/springframework/boot/maven/RepackageMojo.java
+29
-1
No files found.
spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
View file @
ca202ad5
...
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
...
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.file.Files
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Path
;
import
java.nio.file.attribute.FileTime
;
import
java.nio.file.attribute.PosixFilePermission
;
import
java.nio.file.attribute.PosixFilePermission
;
import
java.util.HashSet
;
import
java.util.HashSet
;
import
java.util.Set
;
import
java.util.Set
;
...
@@ -44,6 +45,8 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
...
@@ -44,6 +45,8 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
private
final
JarArchiveOutputStream
jarOutputStream
;
private
final
JarArchiveOutputStream
jarOutputStream
;
private
final
FileTime
lastModifiedTime
;
/**
/**
* Create a new {@link JarWriter} instance.
* Create a new {@link JarWriter} instance.
* @param file the file to write
* @param file the file to write
...
@@ -62,6 +65,21 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
...
@@ -62,6 +65,21 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
* @throws FileNotFoundException if the file cannot be found
* @throws FileNotFoundException if the file cannot be found
*/
*/
public
JarWriter
(
File
file
,
LaunchScript
launchScript
)
throws
FileNotFoundException
,
IOException
{
public
JarWriter
(
File
file
,
LaunchScript
launchScript
)
throws
FileNotFoundException
,
IOException
{
this
(
file
,
launchScript
,
null
);
}
/**
* Create a new {@link JarWriter} instance.
* @param file the file to write
* @param launchScript an optional launch script to prepend to the front of the jar
* @param lastModifiedTime an optional last modified time to apply to the written
* entries
* @throws IOException if the file cannot be opened
* @throws FileNotFoundException if the file cannot be found
* @since 2.3.0
*/
public
JarWriter
(
File
file
,
LaunchScript
launchScript
,
FileTime
lastModifiedTime
)
throws
FileNotFoundException
,
IOException
{
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
file
);
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
file
);
if
(
launchScript
!=
null
)
{
if
(
launchScript
!=
null
)
{
fileOutputStream
.
write
(
launchScript
.
toByteArray
());
fileOutputStream
.
write
(
launchScript
.
toByteArray
());
...
@@ -69,6 +87,7 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
...
@@ -69,6 +87,7 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
}
}
this
.
jarOutputStream
=
new
JarArchiveOutputStream
(
fileOutputStream
);
this
.
jarOutputStream
=
new
JarArchiveOutputStream
(
fileOutputStream
);
this
.
jarOutputStream
.
setEncoding
(
"UTF-8"
);
this
.
jarOutputStream
.
setEncoding
(
"UTF-8"
);
this
.
lastModifiedTime
=
lastModifiedTime
;
}
}
private
void
setExecutableFilePermission
(
File
file
)
{
private
void
setExecutableFilePermission
(
File
file
)
{
...
@@ -85,7 +104,11 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
...
@@ -85,7 +104,11 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
@Override
@Override
protected
void
writeToArchive
(
ZipEntry
entry
,
EntryWriter
entryWriter
)
throws
IOException
{
protected
void
writeToArchive
(
ZipEntry
entry
,
EntryWriter
entryWriter
)
throws
IOException
{
this
.
jarOutputStream
.
putArchiveEntry
(
asJarArchiveEntry
(
entry
));
JarArchiveEntry
jarEntry
=
asJarArchiveEntry
(
entry
);
if
(
this
.
lastModifiedTime
!=
null
)
{
jarEntry
.
setLastModifiedTime
(
this
.
lastModifiedTime
);
}
this
.
jarOutputStream
.
putArchiveEntry
(
jarEntry
);
if
(
entryWriter
!=
null
)
{
if
(
entryWriter
!=
null
)
{
entryWriter
.
write
(
this
.
jarOutputStream
);
entryWriter
.
write
(
this
.
jarOutputStream
);
}
}
...
...
spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
View file @
ca202ad5
...
@@ -18,6 +18,7 @@ package org.springframework.boot.loader.tools;
...
@@ -18,6 +18,7 @@ package org.springframework.boot.loader.tools;
import
java.io.File
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.file.attribute.FileTime
;
import
java.util.jar.JarFile
;
import
java.util.jar.JarFile
;
import
org.springframework.util.Assert
;
import
org.springframework.util.Assert
;
...
@@ -82,6 +83,22 @@ public class Repackager extends Packager {
...
@@ -82,6 +83,22 @@ public class Repackager extends Packager {
* @since 1.3.0
* @since 1.3.0
*/
*/
public
void
repackage
(
File
destination
,
Libraries
libraries
,
LaunchScript
launchScript
)
throws
IOException
{
public
void
repackage
(
File
destination
,
Libraries
libraries
,
LaunchScript
launchScript
)
throws
IOException
{
this
.
repackage
(
destination
,
libraries
,
launchScript
,
null
);
}
/**
* Repackage to the given destination so that it can be launched using '
* {@literal java -jar}'.
* @param destination the destination file (may be the same as the source)
* @param libraries the libraries required to run the archive
* @param launchScript an optional launch script prepended to the front of the jar
* @param lastModifiedTime an optional last modified time to apply to the archive and
* its contents
* @throws IOException if the file cannot be repackaged
* @since 2.3.0
*/
public
void
repackage
(
File
destination
,
Libraries
libraries
,
LaunchScript
launchScript
,
FileTime
lastModifiedTime
)
throws
IOException
{
Assert
.
isTrue
(
destination
!=
null
&&
!
destination
.
isDirectory
(),
"Invalid destination"
);
Assert
.
isTrue
(
destination
!=
null
&&
!
destination
.
isDirectory
(),
"Invalid destination"
);
destination
=
destination
.
getAbsoluteFile
();
destination
=
destination
.
getAbsoluteFile
();
File
source
=
getSource
();
File
source
=
getSource
();
...
@@ -97,7 +114,7 @@ public class Repackager extends Packager {
...
@@ -97,7 +114,7 @@ public class Repackager extends Packager {
destination
.
delete
();
destination
.
delete
();
try
{
try
{
try
(
JarFile
sourceJar
=
new
JarFile
(
workingSource
))
{
try
(
JarFile
sourceJar
=
new
JarFile
(
workingSource
))
{
repackage
(
sourceJar
,
destination
,
libraries
,
launchScript
);
repackage
(
sourceJar
,
destination
,
libraries
,
launchScript
,
lastModifiedTime
);
}
}
}
}
finally
{
finally
{
...
@@ -107,11 +124,14 @@ public class Repackager extends Packager {
...
@@ -107,11 +124,14 @@ public class Repackager extends Packager {
}
}
}
}
private
void
repackage
(
JarFile
sourceJar
,
File
destination
,
Libraries
libraries
,
LaunchScript
launchScript
)
private
void
repackage
(
JarFile
sourceJar
,
File
destination
,
Libraries
libraries
,
LaunchScript
launchScript
,
throws
IOException
{
FileTime
lastModifiedTime
)
throws
IOException
{
try
(
JarWriter
writer
=
new
JarWriter
(
destination
,
launchScript
))
{
try
(
JarWriter
writer
=
new
JarWriter
(
destination
,
launchScript
,
lastModifiedTime
))
{
write
(
sourceJar
,
libraries
,
writer
);
write
(
sourceJar
,
libraries
,
writer
);
}
}
if
(
lastModifiedTime
!=
null
)
{
destination
.
setLastModified
(
lastModifiedTime
.
toMillis
());
}
}
}
private
void
renameFile
(
File
file
,
File
dest
)
{
private
void
renameFile
(
File
file
,
File
dest
)
{
...
...
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle
View file @
ca202ad5
...
@@ -26,6 +26,7 @@ dependencies {
...
@@ -26,6 +26,7 @@ dependencies {
intTestImplementation
(
platform
(
project
(
":spring-boot-project:spring-boot-parent"
)))
intTestImplementation
(
platform
(
project
(
":spring-boot-project:spring-boot-parent"
)))
intTestImplementation
(
project
(
":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"
))
intTestImplementation
(
project
(
":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"
))
intTestImplementation
(
project
(
":spring-boot-project:spring-boot-tools:spring-boot-loader-tools"
))
intTestImplementation
(
project
(
":spring-boot-project:spring-boot-tools:spring-boot-test-support"
))
intTestImplementation
(
project
(
":spring-boot-project:spring-boot-tools:spring-boot-test-support"
))
intTestImplementation
(
"org.apache.maven.shared:maven-invoker"
)
intTestImplementation
(
"org.apache.maven.shared:maven-invoker"
)
intTestImplementation
(
"org.assertj:assertj-core"
)
intTestImplementation
(
"org.assertj:assertj-core"
)
...
...
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java
View file @
ca202ad5
...
@@ -16,10 +16,18 @@
...
@@ -16,10 +16,18 @@
package
org
.
springframework
.
boot
.
maven
;
package
org
.
springframework
.
boot
.
maven
;
import
java.io.File
;
import
java.io.File
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicReference
;
import
java.util.jar.JarFile
;
import
java.util.stream.Collectors
;
import
org.junit.jupiter.api.TestTemplate
;
import
org.junit.jupiter.api.TestTemplate
;
import
org.junit.jupiter.api.extension.ExtendWith
;
import
org.junit.jupiter.api.extension.ExtendWith
;
import
org.springframework.boot.loader.tools.FileUtils
;
import
org.springframework.util.FileSystemUtils
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
/**
...
@@ -316,4 +324,35 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
...
@@ -316,4 +324,35 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
});
});
}
}
@TestTemplate
void
whenJarIsRepackagedWithOutputTimestampConfiguredThenJarIsReproducible
(
MavenBuild
mavenBuild
)
throws
InterruptedException
{
String
firstHash
=
buildJarWithOutputTimestamp
(
mavenBuild
);
Thread
.
sleep
(
1500
);
String
secondHash
=
buildJarWithOutputTimestamp
(
mavenBuild
);
assertThat
(
firstHash
).
isEqualTo
(
secondHash
);
}
private
String
buildJarWithOutputTimestamp
(
MavenBuild
mavenBuild
)
{
AtomicReference
<
String
>
jarHash
=
new
AtomicReference
<>();
mavenBuild
.
project
(
"jar-output-timestamp"
).
execute
((
project
)
->
{
File
repackaged
=
new
File
(
project
,
"target/jar-output-timestamp-0.0.1.BUILD-SNAPSHOT.jar"
);
assertThat
(
repackaged
).
isFile
();
assertThat
(
repackaged
.
lastModified
()).
isEqualTo
(
1584352800000L
);
try
(
JarFile
jar
=
new
JarFile
(
repackaged
))
{
List
<
String
>
unreproducibleEntries
=
jar
.
stream
()
.
filter
((
entry
)
->
entry
.
getLastModifiedTime
().
toMillis
()
!=
1584352800000L
)
.
map
((
entry
)
->
entry
.
getName
()
+
": "
+
entry
.
getLastModifiedTime
())
.
collect
(
Collectors
.
toList
());
assertThat
(
unreproducibleEntries
).
isEmpty
();
jarHash
.
set
(
FileUtils
.
sha1Hash
(
repackaged
));
FileSystemUtils
.
deleteRecursively
(
project
);
}
catch
(
IOException
ex
)
{
throw
new
RuntimeException
(
ex
);
}
});
return
jarHash
.
get
();
}
}
}
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java
View file @
ca202ad5
...
@@ -17,10 +17,18 @@
...
@@ -17,10 +17,18 @@
package
org
.
springframework
.
boot
.
maven
;
package
org
.
springframework
.
boot
.
maven
;
import
java.io.File
;
import
java.io.File
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicReference
;
import
java.util.jar.JarFile
;
import
java.util.stream.Collectors
;
import
org.junit.jupiter.api.TestTemplate
;
import
org.junit.jupiter.api.TestTemplate
;
import
org.junit.jupiter.api.extension.ExtendWith
;
import
org.junit.jupiter.api.extension.ExtendWith
;
import
org.springframework.boot.loader.tools.FileUtils
;
import
org.springframework.util.FileSystemUtils
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
/**
...
@@ -63,4 +71,35 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
...
@@ -63,4 +71,35 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
.
hasEntryWithNameStartingWith
(
"WEB-INF/lib/spring-jcl-"
));
.
hasEntryWithNameStartingWith
(
"WEB-INF/lib/spring-jcl-"
));
}
}
@TestTemplate
void
whenWarIsRepackagedWithOutputTimestampConfiguredThenWarIsReproducible
(
MavenBuild
mavenBuild
)
throws
InterruptedException
{
String
firstHash
=
buildWarWithOutputTimestamp
(
mavenBuild
);
Thread
.
sleep
(
1500
);
String
secondHash
=
buildWarWithOutputTimestamp
(
mavenBuild
);
assertThat
(
firstHash
).
isEqualTo
(
secondHash
);
}
private
String
buildWarWithOutputTimestamp
(
MavenBuild
mavenBuild
)
{
AtomicReference
<
String
>
warHash
=
new
AtomicReference
<>();
mavenBuild
.
project
(
"war-output-timestamp"
).
execute
((
project
)
->
{
File
repackaged
=
new
File
(
project
,
"target/war-output-timestamp-0.0.1.BUILD-SNAPSHOT.war"
);
assertThat
(
repackaged
).
isFile
();
assertThat
(
repackaged
.
lastModified
()).
isEqualTo
(
1584352800000L
);
try
(
JarFile
jar
=
new
JarFile
(
repackaged
))
{
List
<
String
>
unreproducibleEntries
=
jar
.
stream
()
.
filter
((
entry
)
->
entry
.
getLastModifiedTime
().
toMillis
()
!=
1584352800000L
)
.
map
((
entry
)
->
entry
.
getName
()
+
": "
+
entry
.
getLastModifiedTime
())
.
collect
(
Collectors
.
toList
());
assertThat
(
unreproducibleEntries
).
isEmpty
();
warHash
.
set
(
FileUtils
.
sha1Hash
(
repackaged
));
FileSystemUtils
.
deleteRecursively
(
project
);
}
catch
(
IOException
ex
)
{
throw
new
RuntimeException
(
ex
);
}
});
return
warHash
.
get
();
}
}
}
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-output-timestamp/pom.xml
0 → 100644
View file @
ca202ad5
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<groupId>
org.springframework.boot.maven.it
</groupId>
<artifactId>
jar-output-timestamp
</artifactId>
<version>
0.0.1.BUILD-SNAPSHOT
</version>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.build.outputTimestamp>
2020-03-16T02:00:00-08:00
</project.build.outputTimestamp>
<maven.compiler.source>
@java.version@
</maven.compiler.source>
<maven.compiler.target>
@java.version@
</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>
@project.groupId@
</groupId>
<artifactId>
@project.artifactId@
</artifactId>
<version>
@project.version@
</version>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-jar-plugin
</artifactId>
<version>
@maven-jar-plugin.version@
</version>
<configuration>
<archive>
<manifest>
<mainClass>
some.random.Main
</mainClass>
</manifest>
<manifestEntries>
<Not-Used>
Foo
</Not-Used>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>
org.springframework
</groupId>
<artifactId>
spring-context
</artifactId>
<version>
@spring-framework.version@
</version>
</dependency>
<dependency>
<groupId>
jakarta.servlet
</groupId>
<artifactId>
jakarta.servlet-api
</artifactId>
<version>
@jakarta-servlet.version@
</version>
<scope>
provided
</scope>
</dependency>
</dependencies>
</project>
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/jar-output-timestamp/src/main/java/org/test/SampleApplication.java
0 → 100644
View file @
ca202ad5
/*
* Copyright 2012-2020 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
*
* https://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
.
test
;
public
class
SampleApplication
{
public
static
void
main
(
String
[]
args
)
{
}
}
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/war-output-timestamp/pom.xml
0 → 100644
View file @
ca202ad5
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<groupId>
org.springframework.boot.maven.it
</groupId>
<artifactId>
war-output-timestamp
</artifactId>
<version>
0.0.1.BUILD-SNAPSHOT
</version>
<packaging>
war
</packaging>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.build.outputTimestamp>
2020-03-16T02:00:00-08:00
</project.build.outputTimestamp>
<maven.compiler.source>
@java.version@
</maven.compiler.source>
<maven.compiler.target>
@java.version@
</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>
@project.groupId@
</groupId>
<artifactId>
@project.artifactId@
</artifactId>
<version>
@project.version@
</version>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-war-plugin
</artifactId>
<version>
@maven-war-plugin.version@
</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>
@spring-framework.version@
</version>
</dependency>
<dependency>
<groupId>
jakarta.servlet
</groupId>
<artifactId>
jakarta.servlet-api
</artifactId>
<version>
@jakarta-servlet.version@
</version>
<scope>
provided
</scope>
</dependency>
</dependencies>
</project>
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/war-output-timestamp/src/main/java/org/test/SampleApplication.java
0 → 100644
View file @
ca202ad5
/*
* Copyright 2012-2020 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
*
* https://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
.
test
;
public
class
SampleApplication
{
public
static
void
main
(
String
[]
args
)
{
}
}
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/war-output-timestamp/src/main/webapp/index.html
0 → 100644
View file @
ca202ad5
<html></html>
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
View file @
ca202ad5
...
@@ -18,8 +18,11 @@ package org.springframework.boot.maven;
...
@@ -18,8 +18,11 @@ package org.springframework.boot.maven;
import
java.io.File
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.file.attribute.FileTime
;
import
java.time.OffsetDateTime
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Properties
;
import
java.util.Properties
;
import
java.util.concurrent.TimeUnit
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
import
org.apache.maven.artifact.Artifact
;
import
org.apache.maven.artifact.Artifact
;
...
@@ -140,6 +143,15 @@ public class RepackageMojo extends AbstractPackagerMojo {
...
@@ -140,6 +143,15 @@ public class RepackageMojo extends AbstractPackagerMojo {
@Parameter
@Parameter
private
Properties
embeddedLaunchScriptProperties
;
private
Properties
embeddedLaunchScriptProperties
;
/**
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
* (<code>yyyy-MM-dd'T'HH:mm:ssXXX</code>) or an {@code int} representing seconds
* since the epoch.
* @since 2.3.0
*/
@Parameter
(
defaultValue
=
"${project.build.outputTimestamp}"
)
private
String
outputTimestamp
;
@Override
@Override
public
void
execute
()
throws
MojoExecutionException
,
MojoFailureException
{
public
void
execute
()
throws
MojoExecutionException
,
MojoFailureException
{
if
(
this
.
project
.
getPackaging
().
equals
(
"pom"
))
{
if
(
this
.
project
.
getPackaging
().
equals
(
"pom"
))
{
...
@@ -160,7 +172,7 @@ public class RepackageMojo extends AbstractPackagerMojo {
...
@@ -160,7 +172,7 @@ public class RepackageMojo extends AbstractPackagerMojo {
Libraries
libraries
=
getLibraries
(
this
.
requiresUnpack
);
Libraries
libraries
=
getLibraries
(
this
.
requiresUnpack
);
try
{
try
{
LaunchScript
launchScript
=
getLaunchScript
();
LaunchScript
launchScript
=
getLaunchScript
();
repackager
.
repackage
(
target
,
libraries
,
launchScript
);
repackager
.
repackage
(
target
,
libraries
,
launchScript
,
parseOutputTimestamp
()
);
}
}
catch
(
IOException
ex
)
{
catch
(
IOException
ex
)
{
throw
new
MojoExecutionException
(
ex
.
getMessage
(),
ex
);
throw
new
MojoExecutionException
(
ex
.
getMessage
(),
ex
);
...
@@ -168,6 +180,22 @@ public class RepackageMojo extends AbstractPackagerMojo {
...
@@ -168,6 +180,22 @@ public class RepackageMojo extends AbstractPackagerMojo {
updateArtifact
(
source
,
target
,
repackager
.
getBackupFile
());
updateArtifact
(
source
,
target
,
repackager
.
getBackupFile
());
}
}
private
FileTime
parseOutputTimestamp
()
{
// Maven ignore a single-character timestamp as it is "useful to override a full
// value during pom inheritance"
if
(
this
.
outputTimestamp
==
null
||
this
.
outputTimestamp
.
length
()
<
2
)
{
return
null
;
}
long
epochSeconds
;
try
{
epochSeconds
=
Long
.
parseLong
(
this
.
outputTimestamp
);
}
catch
(
NumberFormatException
ex
)
{
epochSeconds
=
OffsetDateTime
.
parse
(
this
.
outputTimestamp
).
toInstant
().
getEpochSecond
();
}
return
FileTime
.
from
(
epochSeconds
,
TimeUnit
.
SECONDS
);
}
/**
/**
* Return the source {@link Artifact} to repackage. If a classifier is specified and
* Return the source {@link Artifact} to repackage. If a classifier is specified and
* an artifact with that classifier exists, it is used. Otherwise, the main artifact
* an artifact with that classifier exists, it is used. Otherwise, the main artifact
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment