Skip to content

Commit 1158d74

Browse files
authored
Merge pull request #12 from sparkoo/checksum_optimization
checksum calculation optimization to lower memory consumption
2 parents 304b6f5 + 5699d82 commit 1158d74

File tree

8 files changed

+86
-48
lines changed

8 files changed

+86
-48
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ $ curl http://localhost:8083/f26
8282
* default value: `disabled`
8383
* when default or `disabled` boxes output json not contains properties `checksumType` and `checksum`
8484
* when `md5|sha1|sha256` boxes output json contains properties `checksumType` and `checksum` with coresponding values
85+
### Advanced Options
86+
* `box.checksum_buffer_size`
87+
* Box file is loaded to this buffer to calculate box checksums
88+
* default value: `1024`
8589

86-
90+
### How to configuration
8791
Configuration can be provided by `application.properties` file on classpath
8892
```
8993
# application.properties

src/main/java/cz/sparko/boxitory/App.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static void main(String[] args) {
2222
@Bean
2323
@Autowired
2424
public BoxRepository boxRepository(AppProperties appProperties) throws NoSuchAlgorithmException {
25-
HashService hashService = HashServiceFactory.createHashService(appProperties.getChecksum());
25+
HashService hashService = HashServiceFactory.createHashService(appProperties);
2626
return new FilesystemBoxRepository(appProperties, hashService);
2727
}
2828
}

src/main/java/cz/sparko/boxitory/conf/AppProperties.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class AppProperties {
1010
private String host_prefix = "";
1111
private String checksum = "disabled";
1212
private boolean sort_desc = false;
13+
private int checksum_buffer_size = 1024;
1314

1415
public String getHome() {
1516
return home;
@@ -27,6 +28,10 @@ public String getChecksum() {
2728
return checksum;
2829
}
2930

31+
public int getChecksum_buffer_size() {
32+
return checksum_buffer_size;
33+
}
34+
3035
public void setSort_desc(boolean sort_desc) {
3136
this.sort_desc = sort_desc;
3237
}
@@ -42,4 +47,8 @@ public void setHost_prefix(String host_prefix) {
4247
public void setChecksum(String checksum) {
4348
this.checksum = checksum;
4449
}
50+
51+
public void setChecksum_buffer_size(int checksum_buffer_size) {
52+
this.checksum_buffer_size = checksum_buffer_size;
53+
}
4554
}

src/main/java/cz/sparko/boxitory/factory/HashServiceFactory.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package cz.sparko.boxitory.factory;
22

3+
import cz.sparko.boxitory.conf.AppProperties;
4+
import cz.sparko.boxitory.service.FilesystemDigestHashService;
35
import cz.sparko.boxitory.service.NoopHashService;
4-
import cz.sparko.boxitory.service.DigestHashService;
56
import cz.sparko.boxitory.service.HashService;
67

78
import java.security.MessageDigest;
89
import java.security.NoSuchAlgorithmException;
910

1011
public class HashServiceFactory {
1112

12-
public static HashService createHashService(String algorithm) throws NoSuchAlgorithmException {
13-
algorithm = algorithm.toUpperCase();
13+
public static HashService createHashService(AppProperties appProperties) throws NoSuchAlgorithmException {
14+
String algorithm = appProperties.getChecksum().toUpperCase();
1415

1516
switch (algorithm) {
1617
case "MD5":
17-
return new DigestHashService(MessageDigest.getInstance(algorithm));
18+
return new FilesystemDigestHashService(MessageDigest.getInstance(algorithm), appProperties);
1819
case "SHA1":
19-
return new DigestHashService(MessageDigest.getInstance("SHA-1"));
20+
return new FilesystemDigestHashService(MessageDigest.getInstance("SHA-1"), appProperties);
2021
case "SHA256":
21-
return new DigestHashService(MessageDigest.getInstance("SHA-256"));
22+
return new FilesystemDigestHashService(MessageDigest.getInstance("SHA-256"), appProperties);
2223
case "DISABLED":
2324
return new NoopHashService();
2425
default:

src/main/java/cz/sparko/boxitory/service/DigestHashService.java renamed to src/main/java/cz/sparko/boxitory/service/FilesystemDigestHashService.java

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
package cz.sparko.boxitory.service;
22

3+
import cz.sparko.boxitory.conf.AppProperties;
34
import org.slf4j.Logger;
45
import org.slf4j.LoggerFactory;
56

67
import javax.xml.bind.DatatypeConverter;
78
import java.io.File;
89
import java.io.IOException;
10+
import java.io.InputStream;
911
import java.nio.file.Files;
1012
import java.security.MessageDigest;
1113
import java.util.Objects;
1214

13-
public class DigestHashService implements HashService {
15+
public class FilesystemDigestHashService implements HashService {
1416

15-
private static final Logger LOG = LoggerFactory.getLogger(DigestHashService.class);
17+
private static final Logger LOG = LoggerFactory.getLogger(FilesystemDigestHashService.class);
1618
private MessageDigest messageDigest;
19+
private int streamBufferLength;
1720

18-
public DigestHashService(MessageDigest messageDigest) {
21+
public FilesystemDigestHashService(MessageDigest messageDigest, AppProperties appProperties) {
1922
this.messageDigest = messageDigest;
23+
streamBufferLength = appProperties.getChecksum_buffer_size();
2024
}
2125

2226
@Override
@@ -26,29 +30,23 @@ public String getHashType() {
2630

2731
@Override
2832
public String getChecksum(String string) {
29-
byte[] bytes;
30-
File file = new File(string);
31-
try {
32-
bytes = getByteArrayFromFile(file);
33+
try (InputStream boxDataStream = Files.newInputStream(new File(string).toPath())) {
34+
LOG.trace("buffering box data (buffer size [{}]b) ...", streamBufferLength);
35+
final byte[] buffer = new byte[streamBufferLength];
36+
int read = boxDataStream.read(buffer, 0, streamBufferLength);
37+
38+
while (read > -1) {
39+
messageDigest.update(buffer, 0, read);
40+
read = boxDataStream.read(buffer, 0, streamBufferLength);
41+
}
3342
} catch (IOException e) {
34-
LOG.error("Error during processing file [{}], message: [{}]", file, e.getMessage());
43+
LOG.error("Error during processing file [{}], message: [{}]", string, e.getMessage());
3544
throw new RuntimeException(
3645
"Error while getting checksum for file " + string + " reason: " + e.getMessage(), e
3746
);
3847
}
3948

40-
return getHash(
41-
getDigestBytes(bytes)
42-
);
43-
}
44-
45-
private byte[] getByteArrayFromFile(File file) throws IOException {
46-
return Files.readAllBytes(file.toPath());
47-
}
48-
49-
private byte[] getDigestBytes(byte[] bytes) {
50-
messageDigest.update(bytes);
51-
return messageDigest.digest();
49+
return getHash(messageDigest.digest());
5250
}
5351

5452
private String getHash(byte[] diggestBytes) {
@@ -57,16 +55,16 @@ private String getHash(byte[] diggestBytes) {
5755

5856
@Override
5957
public String toString() {
60-
return "DigestHashService{" +
58+
return "FilesystemDigestHashService{" +
6159
"messageDigest=" + messageDigest +
6260
'}';
6361
}
6462

6563
@Override
6664
public boolean equals(Object o) {
67-
if (this == o) return true;
68-
if (o == null || getClass() != o.getClass()) return false;
69-
DigestHashService that = (DigestHashService) o;
65+
if (this == o) { return true; }
66+
if (o == null || getClass() != o.getClass()) { return false; }
67+
FilesystemDigestHashService that = (FilesystemDigestHashService) o;
7068
return messageDigest.getAlgorithm().equals(that.messageDigest.getAlgorithm());
7169
}
7270

src/main/resources/logback.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
4+
<!--<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>-->
5+
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
6+
<!--<include resource="org/springframework/boot/logging/logback/file-appender.xml" />-->
7+
8+
<root level="INFO">
9+
<appender-ref ref="CONSOLE" />
10+
<!--<appender-ref ref="FILE" />-->
11+
</root>
12+
13+
<logger name="cz.sparko.boxitory" level="debug" additivity="false">
14+
<appender-ref ref="CONSOLE" />
15+
</logger>
16+
</configuration>

src/test/java/cz/sparko/boxitory/service/DigestHashServiceTest.java renamed to src/test/java/cz/sparko/boxitory/service/FilesystemDigestHashServiceTest.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cz.sparko.boxitory.service;
22

3+
import cz.sparko.boxitory.conf.AppProperties;
34
import org.apache.commons.io.FileUtils;
45
import org.springframework.boot.test.context.SpringBootTest;
56
import org.testng.annotations.AfterClass;
@@ -8,14 +9,15 @@
89
import org.testng.annotations.Test;
910

1011
import java.io.File;
12+
import java.io.FileWriter;
1113
import java.io.IOException;
1214
import java.security.MessageDigest;
1315
import java.security.NoSuchAlgorithmException;
1416

1517
import static org.testng.Assert.assertEquals;
1618

1719
@SpringBootTest
18-
public class DigestHashServiceTest {
20+
public class FilesystemDigestHashServiceTest {
1921

2022
private final String TEST_HOME = "test_repository";
2123
private File testHomeDir;
@@ -37,9 +39,11 @@ private void createTestFolderStructure() throws IOException {
3739
f26.mkdir();
3840
f27.mkdir();
3941

40-
new File(f25.getAbsolutePath() + "/f25_1_virtualbox.box").createNewFile();
41-
new File(f25.getAbsolutePath() + "/f25_2_virtualbox.box").createNewFile();
42-
new File(f25.getAbsolutePath() + "/f25_3_virtualbox.box").createNewFile();
42+
File f25box1 = new File(f25.getAbsolutePath() + "/f25_1_virtualbox.box");
43+
f25box1.createNewFile();
44+
FileWriter fileWriter = new FileWriter(f25box1);
45+
fileWriter.write("123456789\n987654321\nabcdefghi");
46+
fileWriter.close();
4347
}
4448

4549
@AfterClass
@@ -53,24 +57,25 @@ public Object[][] filesAndHashes() {
5357
{
5458
"MD5",
5559
new File(testHomeDir.getAbsolutePath() + "/f25/f25_1_virtualbox.box"),
56-
"d41d8cd98f00b204e9800998ecf8427e"
60+
"86462c346f1358ddbf4f137fb5da43cf"
5761
},
5862
{
5963
"SHA-1",
60-
new File(testHomeDir.getAbsolutePath() + "/f25/f25_2_virtualbox.box"),
61-
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
64+
new File(testHomeDir.getAbsolutePath() + "/f25/f25_1_virtualbox.box"),
65+
"6efeafd3d3304cf5d7fd37db2a7ddbaac09f425d"
6266
},
6367
{
6468
"SHA-256",
65-
new File(testHomeDir.getAbsolutePath() + "/f25/f25_3_virtualbox.box"),
66-
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
69+
new File(testHomeDir.getAbsolutePath() + "/f25/f25_1_virtualbox.box"),
70+
"ae4fe7f29f683d3901d4c620ef2e3c7ed17ebb6813158efd6a16f81b71a0aa43"
6771
}
6872
};
6973
}
7074

7175
@Test(dataProvider = "filesAndHashes")
72-
public void givenHashService_whenGetChecksum_thenChecksumsAreEquals(String algorithm, File file, String expectedChecksum) throws NoSuchAlgorithmException {
73-
HashService hashService = new DigestHashService(MessageDigest.getInstance(algorithm));
76+
public void givenHashService_whenGetChecksum_thenChecksumsAreEquals(String algorithm, File file, String
77+
expectedChecksum) throws NoSuchAlgorithmException {
78+
HashService hashService = new FilesystemDigestHashService(MessageDigest.getInstance(algorithm), new AppProperties());
7479

7580
String checksum = hashService.getChecksum(file.getAbsolutePath());
7681

src/test/java/cz/sparko/boxitory/service/HashServiceFactoryTest.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cz.sparko.boxitory.service;
22

3+
import cz.sparko.boxitory.conf.AppProperties;
34
import cz.sparko.boxitory.factory.HashServiceFactory;
45
import org.springframework.boot.test.context.SpringBootTest;
56
import org.testng.annotations.DataProvider;
@@ -17,22 +18,26 @@ public class HashServiceFactoryTest {
1718
@DataProvider
1819
public Object[][] hashServiceTypes() throws NoSuchAlgorithmException {
1920
return new Object[][]{
20-
{"md5", new DigestHashService(MessageDigest.getInstance("MD5"))},
21-
{"sha1", new DigestHashService(MessageDigest.getInstance("SHA-1"))},
22-
{"sha256", new DigestHashService(MessageDigest.getInstance("SHA-256"))},
21+
{"md5", new FilesystemDigestHashService(MessageDigest.getInstance("MD5"), new AppProperties())},
22+
{"sha1", new FilesystemDigestHashService(MessageDigest.getInstance("SHA-1"), new AppProperties())},
23+
{"sha256", new FilesystemDigestHashService(MessageDigest.getInstance("SHA-256"), new AppProperties())},
2324
{"disabled", new NoopHashService()}
2425
};
2526
}
2627

2728
@Test(dataProvider = "hashServiceTypes")
2829
public void givenFactory_whenCreateHashService_thenGetExpectedInstance(String type, HashService expectedService) throws NoSuchAlgorithmException {
29-
HashService hashService = HashServiceFactory.createHashService(type);
30+
AppProperties appProperties = new AppProperties();
31+
appProperties.setChecksum(type);
32+
HashService hashService = HashServiceFactory.createHashService(appProperties);
3033

3134
assertEquals(hashService, expectedService);
3235
}
3336

3437
@Test(expectedExceptions = IllegalArgumentException.class)
3538
public void givenFactory_whenCreateUnsupportedHashService_thenExceptionIsThrown() throws NoSuchAlgorithmException {
36-
HashService hashService = HashServiceFactory.createHashService("foo");
39+
AppProperties appProperties = new AppProperties();
40+
appProperties.setChecksum("foo");
41+
HashServiceFactory.createHashService(appProperties);
3742
}
3843
}

0 commit comments

Comments
 (0)