Initial commit
This commit is contained in:
parent
1df3e2ad81
commit
6447b1d90b
19 changed files with 565 additions and 54 deletions
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
benchmark
|
||||||
10
.idea/go.imports.xml
generated
Normal file
10
.idea/go.imports.xml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GoImports">
|
||||||
|
<option name="excludedPackages">
|
||||||
|
<array>
|
||||||
|
<option value="golang.org/x/net/context" />
|
||||||
|
</array>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
.idea/gradle.xml
generated
Normal file
17
.idea/gradle.xml
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="myGradleHome" value="" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/misc.xml
generated
Normal file
10
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="homebrew-25" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
build.gradle
Normal file → Executable file
7
build.gradle
Normal file → Executable file
|
|
@ -10,8 +10,15 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation group: 'commons-io', name: 'commons-io', version: '2.21.0'
|
||||||
|
implementation "org.apache.commons:commons-lang3:3.20.0"
|
||||||
|
implementation "org.slf4j:jul-to-slf4j:$sl4j_version"
|
||||||
|
implementation "org.slf4j:log4j-over-slf4j:$sl4j_version"
|
||||||
|
|
||||||
testImplementation platform('org.junit:junit-bom:6.0.0')
|
testImplementation platform('org.junit:junit-bom:6.0.0')
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: '3.0'
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
gradle.properties
Executable file
1
gradle.properties
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
sl4j_version=1.7.35
|
||||||
0
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file → Executable file
0
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file → Executable file
2
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file → Executable file
2
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file → Executable file
|
|
@ -1,4 +1,4 @@
|
||||||
#Wed Apr 08 21:42:31 CEST 2026
|
#Wed Apr 08 22:08:17 CEST 2026
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||||
|
|
|
||||||
0
gradlew.bat
vendored
Normal file → Executable file
0
gradlew.bat
vendored
Normal file → Executable file
2
settings.gradle
Normal file → Executable file
2
settings.gradle
Normal file → Executable file
|
|
@ -1 +1 @@
|
||||||
rootProject.name = 'Benchmark'
|
rootProject.name = 'benchmark'
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package ch.illgner;
|
|
||||||
|
|
||||||
public class OrderCalculator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Berechnet den Endpreis nach Rabatt und inklusive 19% MwSt.
|
|
||||||
*
|
|
||||||
* @param basePrice Der Nettopreis
|
|
||||||
* @param discountRate Der Rabatt (z.B. 0.1 für 10%)
|
|
||||||
* @return Der Endpreis gerundet auf 2 Nachkommastellen
|
|
||||||
*/
|
|
||||||
public double calculateFinalPrice(double basePrice, double discountRate) {
|
|
||||||
double discountedPrice = basePrice * discountRate;
|
|
||||||
|
|
||||||
double priceWithTax = discountedPrice * 1.19;
|
|
||||||
|
|
||||||
return priceWithTax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
138
src/main/java/illgner/ch/createzip/PostBoxZipFileWriter.java
Executable file
138
src/main/java/illgner/ch/createzip/PostBoxZipFileWriter.java
Executable file
|
|
@ -0,0 +1,138 @@
|
||||||
|
package illgner.ch.createzip;
|
||||||
|
|
||||||
|
import illgner.ch.encryption.IOutputStreamEnhancer;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.output.CountingOutputStream;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.PosixFilePermission;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
|
||||||
|
import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ;
|
||||||
|
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
|
||||||
|
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
|
||||||
|
|
||||||
|
public class PostBoxZipFileWriter implements Closeable {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PostBoxZipFileWriter.class);
|
||||||
|
|
||||||
|
private static final Set<PosixFilePermission> ZIP_FILE_PERMISSIONS = EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ);
|
||||||
|
|
||||||
|
private final Path destinationDir;
|
||||||
|
private final String baseName;
|
||||||
|
private final int chunkSize;
|
||||||
|
private final int bufferSize;
|
||||||
|
private final ZipResult zipResult;
|
||||||
|
IOutputStreamEnhancer outputStreamEnhancer;
|
||||||
|
|
||||||
|
private ZipOutputStream zipOutputStream;
|
||||||
|
|
||||||
|
private int fileCount = 1;
|
||||||
|
private long bytesWritten = 0;
|
||||||
|
private File actualZipFile;
|
||||||
|
private CountingOutputStream countingOutputStream;
|
||||||
|
|
||||||
|
public PostBoxZipFileWriter(Path destinationDir, String baseName, int chunkSize, int bufferSize, IOutputStreamEnhancer outputStreamEnhancer) {
|
||||||
|
this.destinationDir = destinationDir;
|
||||||
|
this.baseName = baseName;
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
|
this.bufferSize = bufferSize;
|
||||||
|
this.zipResult = new ZipResult();
|
||||||
|
this.outputStreamEnhancer = outputStreamEnhancer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZipOutputStream createNewZipOutputStream() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
|
||||||
|
String actualZipFilename = createOutputFilename();
|
||||||
|
actualZipFile = destinationDir.resolve(actualZipFilename).toFile();
|
||||||
|
|
||||||
|
LOG.info("Creating zip part {}: {}", fileCount, actualZipFilename);
|
||||||
|
|
||||||
|
countingOutputStream = new CountingOutputStream(outputStreamEnhancer.enhance(new BufferedOutputStream(new FileOutputStream(actualZipFile), bufferSize)));
|
||||||
|
return new ZipOutputStream(countingOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createOutputFilename() {
|
||||||
|
return String.format("%s_%02d.zip", baseName, fileCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(InputStream inputStream, String path) throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
|
||||||
|
if (countingOutputStream == null) {
|
||||||
|
zipOutputStream = createNewZipOutputStream();
|
||||||
|
}
|
||||||
|
if (bytesWritten >= chunkSize) {
|
||||||
|
switchOutputFile();
|
||||||
|
}
|
||||||
|
LOG.info("Writing file {} to zip", path);
|
||||||
|
zipOutputStream.putNextEntry(new ZipEntry(path));
|
||||||
|
bytesWritten += IOUtils.copyLarge(inputStream, zipOutputStream);
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchOutputFile() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
LOG.info("Switching zip");
|
||||||
|
zipOutputStream.flush();
|
||||||
|
zipOutputStream.close();
|
||||||
|
addZipToResult();
|
||||||
|
fileCount++;
|
||||||
|
zipOutputStream = createNewZipOutputStream();
|
||||||
|
bytesWritten = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
LOG.info("Closing zip {}", actualZipFile.getName());
|
||||||
|
zipOutputStream.flush();
|
||||||
|
zipOutputStream.close();
|
||||||
|
addZipToResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeReadMe() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
|
||||||
|
if (zipOutputStream != null) {
|
||||||
|
LOG.info("Skip adding Readme");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream readmeStream = this.getClass().getClassLoader().getResourceAsStream("Readme.txt")) {
|
||||||
|
if (readmeStream == null) {
|
||||||
|
throw new IOException("Cannot open Readme");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Writing Readme");
|
||||||
|
write(readmeStream, "Readme.txt");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("Could not write Readme", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZipResult getZipResult() {
|
||||||
|
return this.zipResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addZipToResult() throws IOException {
|
||||||
|
zipResult.add(actualZipFile.getName(), countingOutputStream.getCount());
|
||||||
|
Files.setPosixFilePermissions(actualZipFile.toPath(), ZIP_FILE_PERMISSIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/illgner/ch/createzip/ZipResult.java
Executable file
23
src/main/java/illgner/ch/createzip/ZipResult.java
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package illgner.ch.createzip;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ZipResult {
|
||||||
|
|
||||||
|
private final List<String> zipFilenames = new ArrayList<>();
|
||||||
|
private final List<Long> zipFileLengths = new ArrayList<>();
|
||||||
|
|
||||||
|
void add(String fileName, long bytesWritten) {
|
||||||
|
zipFilenames.add(fileName);
|
||||||
|
zipFileLengths.add(bytesWritten);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Long> getFileLengths() {
|
||||||
|
return zipFileLengths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFilenames() {
|
||||||
|
return zipFilenames;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/main/java/illgner/ch/encryption/IInputStreamEnhancer.java
Executable file
15
src/main/java/illgner/ch/encryption/IInputStreamEnhancer.java
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package illgner.ch.encryption;
|
||||||
|
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
|
public interface IInputStreamEnhancer {
|
||||||
|
|
||||||
|
InputStream enhance(InputStream inputStream) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException;
|
||||||
|
|
||||||
|
}
|
||||||
18
src/main/java/illgner/ch/encryption/IOutputStreamEnhancer.java
Executable file
18
src/main/java/illgner/ch/encryption/IOutputStreamEnhancer.java
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package illgner.ch.encryption;
|
||||||
|
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
|
public interface IOutputStreamEnhancer {
|
||||||
|
/**
|
||||||
|
* This method enhances the given input stream {@link OutputStream} wrapping with the additional functionality (in our case with encryption)
|
||||||
|
* @param outputStream the original stream {@link OutputStream} to be enhanced.
|
||||||
|
* @return A new {@link OutputStream} that wraps the functionality to input stream.
|
||||||
|
*/
|
||||||
|
OutputStream enhance(OutputStream outputStream) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException;
|
||||||
|
}
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
package ch.illgner;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
public class OrderCalculatorTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCalculateFinalPrice_NormalScenario() {
|
|
||||||
OrderCalculator calculator = new OrderCalculator();
|
|
||||||
|
|
||||||
// Szenario: 100€ Basis, 10% Rabatt = 90€.
|
|
||||||
// 90€ + 19% MwSt (17.10€) = 107.10€
|
|
||||||
double result = calculator.calculateFinalPrice(100.0, 0.10);
|
|
||||||
|
|
||||||
assertEquals(107.10, result, "Der Endpreis für 100€ mit 10% Rabatt muss 107.10 sein.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCalculateFinalPrice_Rounding() {
|
|
||||||
OrderCalculator calculator = new OrderCalculator();
|
|
||||||
|
|
||||||
// Szenario: 10.55€ Basis, 5% Rabatt = 10.0225€
|
|
||||||
// 10.0225€ + 19% MwSt = 11.926775€ -> Gerundet: 11.93€
|
|
||||||
double result = calculator.calculateFinalPrice(10.55, 0.05);
|
|
||||||
|
|
||||||
assertEquals(11.93, result, "Der Preis muss korrekt auf 2 Stellen gerundet werden.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
306
src/test/java/illgner/ch/jobs/postboxbulk/service/PostBoxZipFileWriterTest.java
Executable file
306
src/test/java/illgner/ch/jobs/postboxbulk/service/PostBoxZipFileWriterTest.java
Executable file
|
|
@ -0,0 +1,306 @@
|
||||||
|
package illgner.ch.jobs.postboxbulk.service;
|
||||||
|
|
||||||
|
import illgner.ch.createzip.PostBoxZipFileWriter;
|
||||||
|
import illgner.ch.createzip.ZipResult;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||||
|
import static org.hamcrest.io.FileMatchers.aFileNamed;
|
||||||
|
import static org.hamcrest.io.FileMatchers.anExistingFile;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class PostBoxZipFileWriterTest {
|
||||||
|
|
||||||
|
private static final Path DESTINATION_DIR = Paths.get("./destination");
|
||||||
|
private static final int ONE_BYTE = 1024;
|
||||||
|
private static final int HALF_BYTE = ONE_BYTE / 2;
|
||||||
|
private static final int ZIP_BUFFER_SIZE_IN_BYTE = 2 * ONE_BYTE;
|
||||||
|
private static final int ZIP_CHUNK_SIZE_IN_BYTE = 3 * ONE_BYTE;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setUp() throws IOException {
|
||||||
|
FileUtils.deleteDirectory(DESTINATION_DIR.toFile());
|
||||||
|
boolean directoryCreated = DESTINATION_DIR.toFile().mkdir();
|
||||||
|
assertTrue(directoryCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void cleanUp() throws IOException {
|
||||||
|
FileUtils.deleteDirectory(DESTINATION_DIR.toFile());
|
||||||
|
boolean directoryCreated = DESTINATION_DIR.toFile().mkdir();
|
||||||
|
assertTrue(directoryCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void singleFileSmallerThanChunkSizeToOneZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
byte[] fileSimpleContent = RandomStringUtils.randomAlphanumeric(2 * ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
ZipResult zipResult;
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
String inputFileName = "singleFileSmallerThanChunkSizeToOneZip.txt";
|
||||||
|
PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, outputFileName, ZIP_CHUNK_SIZE_IN_BYTE, ZIP_BUFFER_SIZE_IN_BYTE, (OutputStream outputStream) -> outputStream);
|
||||||
|
try (postBoxZipFileWriter) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(fileSimpleContent), inputFileName);
|
||||||
|
}
|
||||||
|
String expectedZippedFileName = outputFileName + "_01.zip";
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
File createdZipFile = DESTINATION_DIR.resolve(expectedZippedFileName).toFile();
|
||||||
|
|
||||||
|
assertThat(zipResult.getFilenames(), hasSize(1));
|
||||||
|
assertThat(zipResult.getFileLengths(), hasSize(1));
|
||||||
|
assertThat(zipResult.getFilenames().get(0), is(equalTo(expectedZippedFileName)));
|
||||||
|
assertThat(zipResult.getFileLengths().get(0), is(equalTo(createdZipFile.length())));
|
||||||
|
assertThat(createdZipFile, is(anExistingFile()));
|
||||||
|
assertThat(createdZipFile, is(aFileNamed(equalTo(expectedZippedFileName))));
|
||||||
|
|
||||||
|
// check zip content
|
||||||
|
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(createdZipFile))) {
|
||||||
|
assertThat(Objects.requireNonNull(zipInputStream.getNextEntry()).getName(), is(equalTo(inputFileName)));
|
||||||
|
assertThat(IOUtils.toByteArray(zipInputStream), is(equalTo(fileSimpleContent)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void singleFileGreaterThanChunkSizeToOneZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
byte[] fileSimpleContent = RandomStringUtils.randomAlphanumeric(4 * ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
ZipResult zipResult;
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
String inputFileName = "singleFileGreaterThanChunkSizeToOneZip.txt";
|
||||||
|
PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, outputFileName, ZIP_CHUNK_SIZE_IN_BYTE, ZIP_BUFFER_SIZE_IN_BYTE, (OutputStream outputStream) -> outputStream);
|
||||||
|
try (postBoxZipFileWriter) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(fileSimpleContent), inputFileName);
|
||||||
|
}
|
||||||
|
String expectedZippedFileNamePartOne = outputFileName + "_01.zip";
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
assertThat(zipResult.getFilenames(), hasSize(1));
|
||||||
|
assertThat(zipResult.getFileLengths(), hasSize(1));
|
||||||
|
assertTrue(zipResult.getFilenames().contains(expectedZippedFileNamePartOne));
|
||||||
|
|
||||||
|
File createdZipOne = DESTINATION_DIR.resolve(expectedZippedFileNamePartOne).toFile();
|
||||||
|
|
||||||
|
assertThat(createdZipOne, is(anExistingFile()));
|
||||||
|
assertThat(createdZipOne, is(aFileNamed(equalTo(expectedZippedFileNamePartOne))));
|
||||||
|
|
||||||
|
// check zip content
|
||||||
|
extractSplitZipFiles(new File[]{createdZipOne});
|
||||||
|
File extractedFile = new File(DESTINATION_DIR + "/" + inputFileName);
|
||||||
|
byte[] extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(fileSimpleContent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipleFileSmallerThanChunkSizeToSingleZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
byte[] firstFile = RandomStringUtils.randomAlphanumeric(ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] secondFile = "uts öäü ÖÄÜ ß".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
String inputFileNameOne = "multipleFileSmallerThanChunkSizeToSingleZip1.txt";
|
||||||
|
String inputFileNameTwo = "multipleFileSmallerThanChunkSizeToSingleZip2.txt";
|
||||||
|
|
||||||
|
ZipResult zipResult;
|
||||||
|
PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, outputFileName, ZIP_CHUNK_SIZE_IN_BYTE, ZIP_BUFFER_SIZE_IN_BYTE, (OutputStream outputStream) -> outputStream);
|
||||||
|
try (postBoxZipFileWriter) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(firstFile), inputFileNameOne);
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(secondFile), inputFileNameTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
String expectedZippedFileNamePartOne = outputFileName + "_01.zip";
|
||||||
|
File createdZip = DESTINATION_DIR.resolve(expectedZippedFileNamePartOne).toFile();
|
||||||
|
|
||||||
|
assertThat(zipResult.getFilenames(), hasSize(1));
|
||||||
|
assertThat(zipResult.getFileLengths(), hasSize(1));
|
||||||
|
assertThat(createdZip, is(anExistingFile()));
|
||||||
|
assertThat(createdZip, is(aFileNamed(equalTo(expectedZippedFileNamePartOne))));
|
||||||
|
assertThat(zipResult.getFilenames().get(0), is(equalTo(expectedZippedFileNamePartOne)));
|
||||||
|
assertThat(zipResult.getFileLengths().get(0), is(equalTo(createdZip.length())));
|
||||||
|
|
||||||
|
// check zip content
|
||||||
|
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(createdZip))) {
|
||||||
|
assertThat(Objects.requireNonNull(zipInputStream.getNextEntry()).getName(), is(equalTo(inputFileNameOne)));
|
||||||
|
assertThat(IOUtils.toByteArray(zipInputStream), is(equalTo(firstFile)));
|
||||||
|
assertThat(zipInputStream.getNextEntry().getName(), is(equalTo(inputFileNameTwo)));
|
||||||
|
assertThat(IOUtils.toByteArray(zipInputStream), is(equalTo(secondFile)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipleFilesTogetherGreaterThanChunkSizeToSingleZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
String text1 = RandomStringUtils.randomAlphanumeric(2 * ONE_BYTE + HALF_BYTE);
|
||||||
|
String text2 = RandomStringUtils.randomAlphanumeric(2 * ONE_BYTE + HALF_BYTE);
|
||||||
|
byte[] firstFile = text1.getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] secondFile = text2.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
String inputFileNameOne = "multipleFilesTogetherGreaterThanChunkSizeToSingleZip1.txt";
|
||||||
|
String inputFileNameTwo = "multipleFilesTogetherGreaterThanChunkSizeToSingleZip2.txt";
|
||||||
|
|
||||||
|
ZipResult zipResult;
|
||||||
|
PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, outputFileName, ZIP_CHUNK_SIZE_IN_BYTE, ZIP_BUFFER_SIZE_IN_BYTE, (OutputStream outputStream) -> outputStream);
|
||||||
|
try (postBoxZipFileWriter) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(firstFile), inputFileNameOne);
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(secondFile), inputFileNameTwo);
|
||||||
|
} finally {
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
String expectedZippedFileNamePartOne = outputFileName + "_01.zip";
|
||||||
|
|
||||||
|
assertThat(zipResult.getFilenames(), hasSize(1));
|
||||||
|
assertThat(zipResult.getFileLengths(), hasSize(1));
|
||||||
|
assertTrue(zipResult.getFilenames().contains(expectedZippedFileNamePartOne));
|
||||||
|
|
||||||
|
File createdZipOne = DESTINATION_DIR.resolve(expectedZippedFileNamePartOne).toFile();
|
||||||
|
|
||||||
|
assertThat(createdZipOne, is(anExistingFile()));
|
||||||
|
assertThat(createdZipOne, is(aFileNamed(equalTo(expectedZippedFileNamePartOne))));
|
||||||
|
|
||||||
|
// check zip content
|
||||||
|
extractSplitZipFiles(new File[]{createdZipOne});
|
||||||
|
File extractedFile = new File(DESTINATION_DIR + "/" + inputFileNameOne);
|
||||||
|
byte[] extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(firstFile)));
|
||||||
|
|
||||||
|
extractedFile = new File(DESTINATION_DIR + "/" + inputFileNameTwo);
|
||||||
|
extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(secondFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipleBigFilesToMultipleZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
ZipResult zipResult;
|
||||||
|
int bigChuckSize = ONE_BYTE * 50;
|
||||||
|
int bigBufferSize = ONE_BYTE * 10;
|
||||||
|
// First two files will be zipped together and third file will be zipped in the second file.
|
||||||
|
byte[] firstFile = RandomStringUtils.randomAlphanumeric(40 * ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] secondFile = RandomStringUtils.randomAlphanumeric(70 * ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] thirdFile = RandomStringUtils.randomAlphanumeric(10 * ONE_BYTE).getBytes(StandardCharsets.UTF_8);
|
||||||
|
String[] originalFileNames = {"multipleBigFilesToMultipleZip1.txt", "multipleBigFilesToMultipleZip2.txt", "multipleBigFilesToMultipleZip3.txt"};
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
|
||||||
|
PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, outputFileName, bigChuckSize, bigBufferSize, (OutputStream outputStream) -> outputStream);
|
||||||
|
try (postBoxZipFileWriter) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(firstFile), originalFileNames[0]);
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(secondFile), originalFileNames[1]);
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(thirdFile), originalFileNames[2]);
|
||||||
|
} finally {
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
String expectedZippedFileNamePartOne = outputFileName + "_01.zip";
|
||||||
|
String expectedZippedFileNamePartTwo = outputFileName + "_02.zip";
|
||||||
|
|
||||||
|
assertThat(zipResult.getFilenames(), hasSize(2));
|
||||||
|
assertThat(zipResult.getFileLengths(), hasSize(2));
|
||||||
|
assertTrue(zipResult.getFilenames().contains(expectedZippedFileNamePartOne));
|
||||||
|
assertTrue(zipResult.getFilenames().contains(expectedZippedFileNamePartTwo));
|
||||||
|
|
||||||
|
File part1 = DESTINATION_DIR.resolve(expectedZippedFileNamePartOne).toFile();
|
||||||
|
File part2 = DESTINATION_DIR.resolve(expectedZippedFileNamePartTwo).toFile();
|
||||||
|
|
||||||
|
File[] zippedFiles = {part1, part2};
|
||||||
|
|
||||||
|
assertThat(part1, is(anExistingFile()));
|
||||||
|
assertThat(part1, is(aFileNamed(equalTo(expectedZippedFileNamePartOne))));
|
||||||
|
assertThat(part2, is(anExistingFile()));
|
||||||
|
assertThat(part2, is(aFileNamed(equalTo(expectedZippedFileNamePartTwo))));
|
||||||
|
|
||||||
|
extractSplitZipFiles(zippedFiles);
|
||||||
|
|
||||||
|
// check zip content
|
||||||
|
File extractedFile = new File(DESTINATION_DIR + "/" + originalFileNames[0]);
|
||||||
|
byte[] extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(firstFile)));
|
||||||
|
|
||||||
|
extractedFile = new File(DESTINATION_DIR + "/" + originalFileNames[1]);
|
||||||
|
extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(secondFile)));
|
||||||
|
|
||||||
|
extractedFile = new File(DESTINATION_DIR + "/" + originalFileNames[2]);
|
||||||
|
extractedFileBytes = IOUtils.toByteArray(new FileInputStream(extractedFile));
|
||||||
|
assertThat(extractedFileBytes, is(equalTo(thirdFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createBigZip() throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
// first implementation crashed with an OutOfMemoryException while copying an 50MB Stream,
|
||||||
|
// so we added a test for this
|
||||||
|
|
||||||
|
try (PostBoxZipFileWriter zipFileWriter = new PostBoxZipFileWriter(
|
||||||
|
DESTINATION_DIR, "testBigMulti", 100 * 1000 * 1000, ZIP_BUFFER_SIZE_IN_BYTE, (OutputStream outputStream) -> outputStream)) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
byte[] content = RandomStringUtils.randomAlphanumeric(50 * 1000 * 1000).getBytes();
|
||||||
|
zipFileWriter.write(new ByteArrayInputStream(content), "test" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addCompressibleFiles() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
|
||||||
|
byte[] content1 = new byte[10000];
|
||||||
|
byte[] content2 = new byte[10000];
|
||||||
|
byte[] content3 = new byte[10000];
|
||||||
|
|
||||||
|
String outputFileName = RandomStringUtils.randomAlphanumeric(10);
|
||||||
|
|
||||||
|
ZipResult zipResult;
|
||||||
|
try (PostBoxZipFileWriter postBoxZipFileWriter = new PostBoxZipFileWriter(DESTINATION_DIR, outputFileName, 1000, 2048, (OutputStream outputStream) -> outputStream)) {
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(content1), "file1");
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(content2), "file2");
|
||||||
|
postBoxZipFileWriter.write(new ByteArrayInputStream(content3), "file3");
|
||||||
|
zipResult = postBoxZipFileWriter.getZipResult();
|
||||||
|
}
|
||||||
|
assertThat("compressible files only containing zeroes should be compressed into one file", zipResult.getFileLengths().size(), is(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void extractSplitZipFiles(File[] zipFiles) throws IOException {
|
||||||
|
byte[] buffer = new byte[ZIP_BUFFER_SIZE_IN_BYTE];
|
||||||
|
for (File zipFilePath : zipFiles) {
|
||||||
|
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) {
|
||||||
|
ZipEntry zipEntry = zis.getNextEntry();
|
||||||
|
|
||||||
|
while (zipEntry != null) {
|
||||||
|
File newFile = new File(DESTINATION_DIR + "/" + zipEntry.getName());
|
||||||
|
new File(newFile.getParent()).mkdirs(); // Create directories for nested files
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(newFile, true)) {
|
||||||
|
int len;
|
||||||
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
|
fos.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zipEntry = zis.getNextEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue