Initial commit

This commit is contained in:
fillg1 2026-04-09 06:31:16 +02:00
commit 6447b1d90b
19 changed files with 565 additions and 54 deletions

10
.idea/.gitignore generated vendored Normal file
View 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
View file

@ -0,0 +1 @@
benchmark

10
.idea/go.imports.xml generated Normal file
View 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
View 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
View 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
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

9
build.gradle Normal file → Executable file
View file

@ -10,11 +10,18 @@ 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'
} }
test { test {
useJUnitPlatform() useJUnitPlatform()
} }

1
gradle.properties Executable file
View file

@ -0,0 +1 @@
sl4j_version=1.7.35

0
gradle/wrapper/gradle-wrapper.jar vendored Normal file → Executable file
View file

2
gradle/wrapper/gradle-wrapper.properties vendored Normal file → Executable file
View 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
View file

2
settings.gradle Normal file → Executable file
View file

@ -1 +1 @@
rootProject.name = 'Benchmark' rootProject.name = 'benchmark'

View file

@ -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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}

View 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;
}

View file

@ -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.");
}
}

View 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();
}
}
}
}
}