Skip to content

Commit f890c62

Browse files
Merge pull request #1 from aureamunoz/wrong-encoding-49726
Wrong encoding 49726
2 parents 37492b5 + e1831d3 commit f890c62

File tree

33 files changed

+1806
-1871
lines changed

33 files changed

+1806
-1871
lines changed

.github/dependabot.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ updates:
269269
- "org.hibernate.orm*"
270270
- "org.hibernate.reactive*"
271271
- "org.hibernate.search*"
272+
- "org.hibernate.validator*"
272273
ignore:
273274
# Major/minor upgrades require more work and synchronization, we do them manually.
274275
# Only use dependabot for micros (patch versions).

bom/application/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
<jakarta.interceptor-api.version>2.2.0</jakarta.interceptor-api.version>
7171
<jakarta.json-api.version>2.1.3</jakarta.json-api.version>
7272
<jakarta.json.bind-api.version>3.0.1</jakarta.json.bind-api.version>
73-
<jakarta.mail-api.version>2.1.3</jakarta.mail-api.version>
73+
<jakarta.mail-api.version>2.1.4</jakarta.mail-api.version>
7474
<!--jakarta.persistence-api.version is located in the root pom -->
7575
<jakarta.data-api.version>1.0.1</jakarta.data-api.version>
7676
<jakarta.resource-api.version>2.1.0</jakarta.resource-api.version>

core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.quarkus.deployment.pkg;
22

33
import java.nio.file.Path;
4+
import java.time.Instant;
45
import java.util.List;
56
import java.util.Map;
67
import java.util.Optional;
@@ -49,6 +50,13 @@ public interface PackageConfig {
4950
*/
5051
Optional<String> outputName();
5152

53+
/**
54+
* The timestamp used as a reference for generating the packages (e.g. for the creation timestamp of ZIP entries).
55+
* <p>
56+
* The approach is similar to what is done by the maven-jar-plugin with `project.build.outputTimestamp`.
57+
*/
58+
Optional<Instant> outputTimestamp();
59+
5260
/**
5361
* Setting this switch to {@code true} will cause Quarkus to write the transformed application bytecode
5462
* to the build tool's output directory.

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/AbstractJarBuilder.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,6 @@ public AbstractJarBuilder(CurateOutcomeBuildItem curateOutcome,
7070
this.removedArtifactKeys = removedArtifactKeys;
7171
}
7272

73-
protected static ArchiveCreator newArchiveCreator(Path archivePath, PackageConfig config) throws IOException {
74-
return new ZipFileSystemArchiveCreator(archivePath, config.jar().compress());
75-
}
76-
7773
/**
7874
* Copy files from {@code archive} to {@code fs}, filtering out service providers into the given map.
7975
*

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/AbstractLegacyThinJarBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public AbstractLegacyThinJarBuilder(CurateOutcomeBuildItem curateOutcome,
6060

6161
protected void doBuild(Path runnerJar, Path libDir) throws IOException {
6262
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(runnerJar,
63-
packageConfig.jar().compress(), outputTarget.getOutputDirectory(), executorService)) {
63+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null), outputTarget.getOutputDirectory(),
64+
executorService)) {
6465
final Map<String, String> seen = new HashMap<>();
6566
final StringBuilder classPath = new StringBuilder();
6667
final Map<String, List<byte[]>> services = new HashMap<>();

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/FastJarBuilder.java

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashSet;
2828
import java.util.List;
2929
import java.util.Map;
30+
import java.util.Map.Entry;
3031
import java.util.Set;
3132
import java.util.concurrent.ExecutorService;
3233
import java.util.function.Consumer;
@@ -161,10 +162,15 @@ public JarBuildItem build() throws IOException {
161162
Path transformedZip = quarkus.resolve(FastJarFormat.TRANSFORMED_BYTECODE_JAR);
162163
fastJarJarsBuilder.setTransformedJar(transformedZip);
163164
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(transformedZip,
164-
packageConfig.jar().compress(), outputTarget.getOutputDirectory(), executorService)) {
165-
for (Set<TransformedClass> transformedSet : transformedClasses
166-
.getTransformedClassesByJar().values()) {
167-
for (TransformedClass transformed : transformedSet) {
165+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null),
166+
outputTarget.getOutputDirectory(), executorService)) {
167+
// we make sure the entries are added in a reproducible order
168+
// we use Path#toString() to get a reproducible order on both Unix-based OSes and Windows
169+
for (Entry<Path, Set<TransformedClass>> transformedClassEntry : transformedClasses
170+
.getTransformedClassesByJar().entrySet().stream()
171+
.sorted(Comparator.comparing(e -> e.getKey().toString())).toList()) {
172+
for (TransformedClass transformed : transformedClassEntry.getValue().stream()
173+
.sorted(Comparator.comparing(TransformedClass::getFileName)).toList()) {
168174
if (transformed.getData() != null) {
169175
archiveCreator.addFile(transformed.getData(), transformed.getFileName());
170176
}
@@ -179,13 +185,18 @@ public JarBuildItem build() throws IOException {
179185
Path generatedZip = quarkus.resolve(FastJarFormat.GENERATED_BYTECODE_JAR);
180186
fastJarJarsBuilder.setGeneratedJar(generatedZip);
181187
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(generatedZip,
182-
packageConfig.jar().compress(), outputTarget.getOutputDirectory(), executorService)) {
183-
for (GeneratedClassBuildItem i : generatedClasses) {
184-
String fileName = fromClassNameToResourceName(i.getName());
188+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null), outputTarget.getOutputDirectory(),
189+
executorService)) {
190+
// make sure we write the elements in order
191+
for (GeneratedClassBuildItem i : generatedClasses.stream()
192+
.sorted(Comparator.comparing(GeneratedClassBuildItem::binaryName)).toList()) {
193+
String fileName = fromClassNameToResourceName(i.internalName());
185194
archiveCreator.addFile(i.getClassData(), fileName);
186195
}
187196

188-
for (GeneratedResourceBuildItem i : generatedResources) {
197+
// make sure we write the elements in order
198+
for (GeneratedResourceBuildItem i : generatedResources.stream()
199+
.sorted(Comparator.comparing(GeneratedResourceBuildItem::getName)).toList()) {
189200
archiveCreator.addFile(i.getData(), i.getName());
190201
}
191202
}
@@ -207,7 +218,8 @@ public JarBuildItem build() throws IOException {
207218
.setPath(runnerJar));
208219
Predicate<String> ignoredEntriesPredicate = getThinJarIgnoredEntriesPredicate(packageConfig);
209220
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(runnerJar,
210-
packageConfig.jar().compress(), outputTarget.getOutputDirectory(), executorService)) {
221+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null),
222+
outputTarget.getOutputDirectory(), executorService)) {
211223
copyFiles(applicationArchives.getRootArchive(), archiveCreator, null, ignoredEntriesPredicate);
212224
}
213225
}
@@ -297,7 +309,8 @@ public JarBuildItem build() throws IOException {
297309
}
298310
if (!rebuild) {
299311
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(initJar,
300-
packageConfig.jar().compress(), outputTarget.getOutputDirectory(), executorService)) {
312+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null),
313+
outputTarget.getOutputDirectory(), executorService)) {
301314
ResolvedDependency appArtifact = curateOutcome.getApplicationModel().getAppArtifact();
302315
generateManifest(archiveCreator, classPath.toString(), packageConfig, appArtifact,
303316
QuarkusEntryPoint.class.getName(),
@@ -453,7 +466,8 @@ private static void copyDependency(Set<ArtifactKey> parentFirstArtifacts, Output
453466
private static void packageClasses(Path resolvedDep, final Path targetPath, PackageConfig packageConfig,
454467
OutputTargetBuildItem outputTargetBuildItem, ExecutorService executorService) throws IOException {
455468
try (ArchiveCreator archiveCreator = new ParallelCommonsCompressArchiveCreator(targetPath,
456-
packageConfig.jar().compress(), outputTargetBuildItem.getOutputDirectory(), executorService)) {
469+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null),
470+
outputTargetBuildItem.getOutputDirectory(), executorService)) {
457471
Files.walkFileTree(resolvedDep, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
458472
new SimpleFileVisitor<Path>() {
459473
@Override

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/ParallelCommonsCompressArchiveCreator.java

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import java.io.UncheckedIOException;
77
import java.nio.file.Files;
88
import java.nio.file.Path;
9+
import java.nio.file.attribute.FileTime;
10+
import java.time.Instant;
911
import java.util.Collection;
1012
import java.util.HashMap;
1113
import java.util.List;
@@ -27,11 +29,6 @@
2729
import org.apache.commons.compress.archivers.zip.ZipArchiveEntryRequest;
2830
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
2931
import org.apache.commons.compress.parallel.InputStreamSupplier;
30-
import org.jboss.threads.EnhancedQueueExecutor;
31-
import org.jboss.threads.JBossExecutors;
32-
import org.jboss.threads.JBossThreadFactory;
33-
34-
import io.smallrye.common.cpu.ProcessorInfo;
3532

3633
/**
3734
* This ArchiveCreator may not be used to build Uberjars.
@@ -48,6 +45,7 @@ public class ParallelCommonsCompressArchiveCreator implements ArchiveCreator {
4845
private static final InputStreamSupplier EMPTY_SUPPLIER = () -> new ByteArrayInputStream(new byte[0]);
4946

5047
private final Path archivePath;
48+
private final FileTime entryTimestamp;
5149
private final ZipArchiveOutputStream archive;
5250
private final ParallelScatterZipCreator scatterZipCreator;
5351
private final ScatterZipOutputStream directories;
@@ -56,7 +54,7 @@ public class ParallelCommonsCompressArchiveCreator implements ArchiveCreator {
5654

5755
private final Map<String, String> addedFiles = new HashMap<>();
5856

59-
ParallelCommonsCompressArchiveCreator(Path archivePath, boolean compressed, Path outputTarget,
57+
ParallelCommonsCompressArchiveCreator(Path archivePath, boolean compressed, Instant entryTimestamp, Path outputTarget,
6058
ExecutorService executorService) throws IOException {
6159
int compressionLevel;
6260
if (compressed) {
@@ -68,6 +66,7 @@ public class ParallelCommonsCompressArchiveCreator implements ArchiveCreator {
6866
}
6967

7068
this.archivePath = archivePath;
69+
this.entryTimestamp = entryTimestamp != null ? FileTime.from(entryTimestamp) : null;
7170
this.archive = new ZipArchiveOutputStream(archivePath);
7271
this.archive.setMethod(compressionMethod);
7372
this.tempDirectory = Files.createTempDirectory(outputTarget, "zip-builder-files");
@@ -84,14 +83,17 @@ public class ParallelCommonsCompressArchiveCreator implements ArchiveCreator {
8483
@Override
8584
public void addManifest(Manifest manifest) throws IOException {
8685
// we add the manifest directly to the final archive to make sure it is the first element added
87-
archive.putArchiveEntry(new ZipArchiveEntry("META-INF/"));
86+
ZipArchiveEntry metaInfArchiveEntry = new ZipArchiveEntry("META-INF/");
87+
normalizeTimestamps(metaInfArchiveEntry);
88+
archive.putArchiveEntry(metaInfArchiveEntry);
8889
archive.closeArchiveEntry();
8990

9091
ByteArrayOutputStream baos = new ByteArrayOutputStream();
9192
manifest.write(baos);
9293
byte[] manifestBytes = baos.toByteArray();
9394

9495
ZipArchiveEntry manifestEntry = new ZipArchiveEntry("META-INF/MANIFEST.MF");
96+
normalizeTimestamps(manifestEntry);
9597
manifestEntry.setSize(manifestBytes.length);
9698
archive.putArchiveEntry(manifestEntry);
9799
archive.write(manifestBytes);
@@ -165,11 +167,13 @@ private void addDirectoryEntry(final ZipArchiveEntry zipArchiveEntry) throws IOE
165167
if (!zipArchiveEntry.isDirectory() || zipArchiveEntry.isUnixSymlink()) {
166168
return;
167169
}
170+
normalizeTimestamps(zipArchiveEntry);
168171
zipArchiveEntry.setMethod(compressionMethod);
169172
directories.addArchiveEntry(ZipArchiveEntryRequest.createZipArchiveEntryRequest(zipArchiveEntry, EMPTY_SUPPLIER));
170173
}
171174

172175
private void addEntry(final ZipArchiveEntry zipArchiveEntry, final InputStreamSupplier streamSupplier) throws IOException {
176+
normalizeTimestamps(zipArchiveEntry);
173177
zipArchiveEntry.setMethod(compressionMethod);
174178
if (zipArchiveEntry.isDirectory() && !zipArchiveEntry.isUnixSymlink()) {
175179
directories.addArchiveEntry(ZipArchiveEntryRequest.createZipArchiveEntryRequest(zipArchiveEntry, streamSupplier));
@@ -204,17 +208,15 @@ private static InputStreamSupplier toInputStreamSupplier(Path path) {
204208
};
205209
}
206210

207-
private static ExecutorService initExecutorService() {
208-
final EnhancedQueueExecutor.Builder executorServiceBuilder = new EnhancedQueueExecutor.Builder();
209-
executorServiceBuilder.setRegisterMBean(false);
210-
executorServiceBuilder.setQueueLimited(false);
211-
executorServiceBuilder.setCorePoolSize(8).setMaximumPoolSize(ProcessorInfo.availableProcessors() * 2);
212-
executorServiceBuilder.setExceptionHandler(JBossExecutors.loggingExceptionHandler());
213-
executorServiceBuilder
214-
.setThreadFactory(
215-
new JBossThreadFactory(new ThreadGroup("jar-compress group"), Boolean.FALSE, null, "jar-compress-%t",
216-
JBossExecutors.loggingExceptionHandler(), null));
217-
return executorServiceBuilder.build();
211+
private void normalizeTimestamps(ZipArchiveEntry archiveEntry) {
212+
if (entryTimestamp == null) {
213+
return;
214+
}
215+
216+
archiveEntry.setTime(entryTimestamp.toMillis());
217+
archiveEntry.setCreationTime(entryTimestamp);
218+
archiveEntry.setLastModifiedTime(entryTimestamp);
219+
archiveEntry.setLastAccessTime(entryTimestamp);
218220
}
219221

220222
@Override

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/UberJarBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public JarBuildItem build() throws IOException {
142142

143143
private void buildUberJar0(Path runnerJar) throws IOException {
144144
try (ZipFileSystemArchiveCreator archiveCreator = new ZipFileSystemArchiveCreator(runnerJar,
145-
packageConfig.jar().compress())) {
145+
packageConfig.jar().compress(), packageConfig.outputTimestamp().orElse(null))) {
146146
LOG.info("Building uber jar: " + runnerJar);
147147

148148
final Map<String, String> seen = new HashMap<>();
@@ -318,6 +318,7 @@ private static class IsEntryIgnoredForUberJarPredicate implements Predicate<Stri
318318
"META-INF/jandex.idx",
319319
"META-INF/panache-archive.marker", // deprecated and unused, but still present in some archives
320320
"META-INF/build.metadata", // present in the Red Hat Build of Quarkus
321+
"META-INF/quarkus-config-doc/quarkus-config-javadoc.json",
321322
"LICENSE");
322323

323324
@Override

core/deployment/src/main/java/io/quarkus/deployment/pkg/jar/ZipFileSystemArchiveCreator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.nio.file.Files;
1111
import java.nio.file.Path;
1212
import java.nio.file.StandardCopyOption;
13+
import java.time.Instant;
1314
import java.util.HashMap;
1415
import java.util.List;
1516
import java.util.Map;
@@ -29,12 +30,13 @@ class ZipFileSystemArchiveCreator implements ArchiveCreator {
2930

3031
private final Map<String, String> addedFiles = new HashMap<>();
3132

32-
public ZipFileSystemArchiveCreator(Path archivePath, boolean compressed) {
33+
public ZipFileSystemArchiveCreator(Path archivePath, boolean compressed, Instant entryTimestamp) {
3334
try {
3435
if (compressed) {
35-
zipFileSystem = ZipUtils.newZip(archivePath);
36+
zipFileSystem = ZipUtils.createNewReproducibleZipFileSystem(archivePath, entryTimestamp);
3637
} else {
37-
zipFileSystem = ZipUtils.newZip(archivePath, Map.of("compressionMethod", "STORED"));
38+
zipFileSystem = ZipUtils.createNewReproducibleZipFileSystem(archivePath, Map.of("compressionMethod", "STORED"),
39+
entryTimestamp);
3840
}
3941
} catch (IOException e) {
4042
throw new RuntimeException("Unable to initialize ZipFileSystem: " + archivePath, e);

devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ public Properties getBuildSystemProperties(QuarkusBootstrapMojo mojo, boolean qu
348348

349349
effectiveProperties.putIfAbsent("quarkus.application.name", mojo.mavenProject().getArtifactId());
350350
effectiveProperties.putIfAbsent("quarkus.application.version", mojo.mavenProject().getVersion());
351+
// pass the project.build.outputTimestamp to Quarkus packaging subsystem
352+
if (mojo.mavenProject().getProperties().containsKey("project.build.outputTimestamp")) {
353+
effectiveProperties.putIfAbsent("quarkus.package.output-timestamp",
354+
mojo.mavenProject().getProperties().getProperty("project.build.outputTimestamp"));
355+
}
351356

352357
for (Map.Entry<String, String> attribute : mojo.manifestEntries().entrySet()) {
353358
if (attribute.getValue() == null) {

0 commit comments

Comments
 (0)