Skip to content

Commit 55d7874

Browse files
authored
convert File History cache serialization to Smile (#4268)
fixes #3539
1 parent d2e8d64 commit 55d7874

File tree

11 files changed

+232
-210
lines changed

11 files changed

+232
-210
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/history/AbstractCache.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
2222
*/
2323
package org.opengrok.indexer.history;
2424

@@ -132,6 +132,6 @@ public void clearFile(String path) {
132132
}
133133

134134
public String getCacheFileSuffix() {
135-
return ".gz";
135+
return "";
136136
}
137137
}

opengrok-indexer/src/main/java/org/opengrok/indexer/history/CacheUtil.java

-18
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,12 @@
2929
import org.opengrok.indexer.util.ForbiddenSymlinkException;
3030
import org.opengrok.indexer.util.IOUtils;
3131

32-
import java.beans.XMLEncoder;
33-
import java.io.BufferedOutputStream;
3432
import java.io.File;
35-
import java.io.FileOutputStream;
3633
import java.io.IOException;
3734
import java.nio.file.NoSuchFileException;
3835
import java.nio.file.Paths;
3936
import java.util.logging.Level;
4037
import java.util.logging.Logger;
41-
import java.util.zip.GZIPOutputStream;
4238

4339
/**
4440
* Helper functions for {@link HistoryCache} and {@link AnnotationCache} implementations.
@@ -51,20 +47,6 @@ private CacheUtil() {
5147
// private to enforce static
5248
}
5349

54-
/**
55-
* Write serialized object to file.
56-
* @param object object to be stored
57-
* @param output output file
58-
* @throws IOException on error
59-
*/
60-
public static void writeCache(Object object, File output) throws IOException {
61-
try (FileOutputStream out = new FileOutputStream(output);
62-
XMLEncoder e = new XMLEncoder(new GZIPOutputStream(new BufferedOutputStream(out)))) {
63-
e.setPersistenceDelegate(File.class, new FileHistoryCache.FilePersistenceDelegate());
64-
e.writeObject(object);
65-
}
66-
}
67-
6850
/**
6951
*
7052
* @param repository {@link RepositoryInfo} instance

opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileHistoryCache.java

+162-121
Large diffs are not rendered by default.

opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryEntry.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright (c) 2019, Chris Fraire <[email protected]>.
2323
*/
2424
package org.opengrok.indexer.history;
@@ -32,6 +32,7 @@
3232
import java.util.logging.Level;
3333
import java.util.logging.Logger;
3434

35+
import com.fasterxml.jackson.annotation.JsonIgnore;
3536
import org.jetbrains.annotations.VisibleForTesting;
3637
import org.opengrok.indexer.logger.LoggerFactory;
3738

@@ -55,6 +56,7 @@ public class HistoryEntry implements Serializable {
5556
private final StringBuffer message;
5657

5758
private boolean active;
59+
@JsonIgnore
5860
private SortedSet<String> files;
5961

6062
/** Creates a new instance of HistoryEntry. */
@@ -94,6 +96,7 @@ public HistoryEntry(String revision, Date date, String author, String message, b
9496
this.revision = revision;
9597
}
9698

99+
@JsonIgnore
97100
public String getLine() {
98101
return String.join(" ",
99102
getRevision(), getDate().toString(), getAuthor(), message, "\n");

opengrok-indexer/src/main/java/org/opengrok/indexer/history/RepositoryWithHistoryTraversal.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ protected void doCreateCache(HistoryCache cache, String sinceRevision, File dire
145145
if (fileCollector != null) {
146146
visitors.add(fileCollector);
147147
}
148-
try (Progress progress = new Progress(LOGGER, String.format(" changesets traversed of %s", this))) {
148+
try (Progress progress = new Progress(LOGGER, String.format("changesets traversed of %s", this),
149+
Level.FINER)) {
149150
ProgressVisitor progressVisitor = new ProgressVisitor(progress);
150151
visitors.add(progressVisitor);
151152
traverseHistory(directory, sinceRevision, null, null, visitors);
@@ -186,7 +187,8 @@ protected void doCreateCache(HistoryCache cache, String sinceRevision, File dire
186187
}
187188

188189
try (Progress progress = new Progress(LOGGER,
189-
String.format(" changesets traversed of %s (range %s %s)", this, sinceRevision, tillRevision))) {
190+
String.format("changesets traversed of %s (range %s %s)", this, sinceRevision, tillRevision),
191+
Level.FINER)) {
190192
ProgressVisitor progressVisitor = new ProgressVisitor(progress);
191193
visitors.add(progressVisitor);
192194
traverseHistory(directory, sinceRevision, tillRevision, null, visitors);

opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public Progress(Logger logger, String suffix, long totalCount, Level logLevel) {
105105
Level.FINE, Level.FINER, Level.FINEST, Level.ALL);
106106
int i = standardLevels.indexOf(baseLogLevel);
107107
for (int num : new int[]{100, 50, 10, 1}) {
108+
if (i >= standardLevels.size()) {
109+
break;
110+
}
111+
108112
Level level = standardLevels.get(i);
109113
if (level == null) {
110114
break;

opengrok-indexer/src/test/java/org/opengrok/indexer/history/FileHistoryCacheTest.java

+47-58
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright (c) 2018, 2020, Chris Fraire <[email protected]>.
2323
* Portions Copyright (c) 2020, 2023, Ric Harris <[email protected]>.
2424
*/
@@ -28,7 +28,6 @@
2828
import static org.junit.jupiter.api.Assertions.assertFalse;
2929
import static org.junit.jupiter.api.Assertions.assertNotNull;
3030
import static org.junit.jupiter.api.Assertions.assertNull;
31-
import static org.junit.jupiter.api.Assertions.assertThrows;
3231
import static org.junit.jupiter.api.Assertions.assertTrue;
3332
import static org.opengrok.indexer.condition.RepositoryInstalled.Type.MERCURIAL;
3433
import static org.opengrok.indexer.condition.RepositoryInstalled.Type.SCCS;
@@ -38,18 +37,26 @@
3837
import java.io.File;
3938
import java.io.FileOutputStream;
4039
import java.io.IOException;
40+
import java.io.OutputStream;
4141
import java.nio.charset.StandardCharsets;
4242
import java.nio.file.Files;
4343
import java.nio.file.Path;
4444
import java.nio.file.Paths;
4545
import java.nio.file.attribute.FileTime;
46+
import java.util.ArrayList;
47+
import java.util.Collections;
4648
import java.util.Date;
4749
import java.util.Iterator;
4850
import java.util.LinkedList;
4951
import java.util.List;
5052
import java.util.Map;
5153
import java.util.Set;
5254

55+
import com.fasterxml.jackson.databind.ObjectMapper;
56+
import com.fasterxml.jackson.databind.ObjectWriter;
57+
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
58+
import com.fasterxml.jackson.dataformat.smile.SmileParser;
59+
import com.fasterxml.jackson.dataformat.smile.databind.SmileMapper;
5360
import org.apache.commons.lang3.time.DateUtils;
5461
import org.eclipse.jgit.api.Git;
5562
import org.junit.jupiter.api.AfterEach;
@@ -196,7 +203,7 @@ void testStoreTouchGet() throws Exception {
196203
/*
197204
* The history should not be disturbed.
198205
* Make sure that get() retrieved the history from cache. Mocking/spying static methods
199-
* (FileHistoryCache#readCache() in this case) is tricky so use the cache hits metric.
206+
* (FileHistoryCache#readHistory() in this case) is tricky so use the cache hits metric.
200207
*/
201208
double cacheHitsBeforeGet = cache.getFileHistoryCacheHits();
202209
History historyAfterReindex = cache.get(file, repo, false);
@@ -251,7 +258,7 @@ void testStoreAndGetIncrementalTags() throws Exception {
251258
// Avoid uncommitted changes.
252259
MercurialRepositoryTest.runHgCommand(reposRoot, "revert", "--all");
253260

254-
// Add bunch of changesets with file based changes and tags.
261+
// Add a bunch of changesets with file based changes and tags.
255262
MercurialRepositoryTest.runHgCommand(reposRoot, "import",
256263
Paths.get(getClass().getResource("/history/hg-export-tag.txt").toURI()).toString());
257264

@@ -396,14 +403,15 @@ void testStoreAndGet() throws Exception {
396403
/**
397404
* Check how incremental reindex behaves when indexing changesets that
398405
* rename+change file.
399-
*
406+
* <p>
400407
* The scenario goes as follows:
401408
* - create Mercurial repository
402409
* - perform full reindex
403410
* - add changesets which renamed and modify a file
404411
* - perform incremental reindex
405412
* - change+rename the file again
406413
* - incremental reindex
414+
* </p>
407415
*/
408416
@EnabledOnOs({OS.LINUX, OS.MAC, OS.SOLARIS, OS.AIX, OS.OTHER})
409417
@EnabledForRepository(MERCURIAL)
@@ -984,59 +992,40 @@ void testStoreAndTryToGetIgnored() throws Exception {
984992
assertNotNull(retrievedHistory, "history for Makefile should not be null");
985993
}
986994

987-
@ParameterizedTest
988-
@ValueSource(strings = {
989-
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
990-
"<java version=\"11.0.8\" class=\"java.beans.XMLDecoder\">\n" +
991-
" <object class=\"java.lang.Runtime\" method=\"getRuntime\">\n" +
992-
" <void method=\"exec\">\n" +
993-
" <array class=\"java.lang.String\" length=\"2\">\n" +
994-
" <void index=\"0\">\n" +
995-
" <string>/usr/bin/nc</string>\n" +
996-
" </void>\n" +
997-
" <void index=\"1\">\n" +
998-
" <string>-l</string>\n" +
999-
" </void>\n" +
1000-
" </array>\n" +
1001-
" </void>\n" +
1002-
" </object>\n" +
1003-
"</java>",
1004-
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
1005-
"<java version=\"11.0.8\" class=\"java.beans.XMLDecoder\">\n" +
1006-
" <object class=\"java.lang.ProcessBuilder\">\n" +
1007-
" <array class=\"java.lang.String\" length=\"1\" >\n" +
1008-
" <void index=\"0\"> \n" +
1009-
" <string>/usr/bin/curl https://oracle.com</string>\n" +
1010-
" </void>\n" +
1011-
" </array>\n" +
1012-
" <void method=\"start\"/>\n" +
1013-
" </object>\n" +
1014-
"</java>",
1015-
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
1016-
"<java version=\"11.0.8\" class=\"java.beans.XMLDecoder\">\n" +
1017-
" <object class = \"java.io.FileOutputStream\"> \n" +
1018-
" <string>opengrok_test.txt</string>\n" +
1019-
" <method name = \"write\">\n" +
1020-
" <array class=\"byte\" length=\"3\">\n" +
1021-
" <void index=\"0\"><byte>96</byte></void>\n" +
1022-
" <void index=\"1\"><byte>96</byte></void>\n" +
1023-
" <void index=\"2\"><byte>96</byte></void>\n" +
1024-
" </array>\n" +
1025-
" </method>\n" +
1026-
" <method name=\"close\"/>\n" +
1027-
" </object>\n" +
1028-
"</java>"
1029-
})
1030-
void testDeserializationOfNotWhiteListedClassThrowsError(final String exploit) {
1031-
assertThrows(IllegalAccessError.class, () -> FileHistoryCache.readCache(exploit));
1032-
}
1033-
995+
// TODO
1034996
@Test
1035-
void testReadCacheValid() throws IOException {
1036-
File testFile = new File(FileHistoryCacheTest.class.getClassLoader().
1037-
getResource("history/FileHistoryCache.java.gz").getFile());
1038-
History history = FileHistoryCache.readCache(testFile);
1039-
assertNotNull(history);
1040-
assertEquals(30, history.getHistoryEntries().size());
997+
void testSmile() throws Exception {
998+
ObjectMapper mapper = new SmileMapper();
999+
File outputFile = new File("/tmp/histentry");
1000+
ObjectWriter objectWriter = mapper.writer().forType(HistoryEntry.class);
1001+
1002+
try (OutputStream outputStream = new FileOutputStream(outputFile)) {
1003+
HistoryEntry historyEntry = new HistoryEntry("1.1.1", "1",
1004+
new Date(1245446973L / 60 * 60 * 1000),
1005+
"xyz",
1006+
"Return failure when executed with no arguments",
1007+
true, Collections.emptyList());
1008+
byte[] bytes = objectWriter.writeValueAsBytes(historyEntry);
1009+
outputStream.write(bytes);
1010+
1011+
historyEntry = new HistoryEntry("2.2.2", "2",
1012+
new Date(1245446973L / 60 * 60 * 1000),
1013+
"xyz",
1014+
"Return failure when executed with no arguments",
1015+
true, Collections.emptyList());
1016+
bytes = objectWriter.writeValueAsBytes(historyEntry);
1017+
outputStream.write(bytes);
1018+
}
1019+
1020+
SmileFactory factory = new SmileFactory();
1021+
List<HistoryEntry> historyEntryList = new ArrayList<>();
1022+
try (SmileParser parser = factory.createParser(outputFile)) {
1023+
parser.setCodec(mapper);
1024+
Iterator<HistoryEntry> historyEntryIterator = parser.readValuesAs(HistoryEntry.class);
1025+
historyEntryIterator.forEachRemaining(historyEntryList::add);
1026+
}
1027+
1028+
History history = new History(historyEntryList);
1029+
System.out.println(history);
10411030
}
10421031
}

opengrok-indexer/src/test/java/org/opengrok/indexer/history/HistoryTest.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,16 @@ void testGetLastHistoryEntry() {
145145
}
146146

147147
/**
148-
* Serialises and then deserialises a history and checks for equality.
148+
* Serialises and then deserializes a history and checks for equality.
149149
*/
150150
@Test
151151
void testSerialisation() throws IOException {
152152
ArrayList<HistoryEntry> serialisableEntryList = new ArrayList<>(entries);
153153
History history = new History(serialisableEntryList);
154-
File tempFile = File.createTempFile("tmpHistory1", "gz", temporaryPath.toFile());
155-
CacheUtil.writeCache(history, tempFile);
156-
History deserialised = FileHistoryCache.readCache(tempFile);
157-
assertEquals(history, deserialised);
154+
File tempFile = File.createTempFile("tmpHistory1", "", temporaryPath.toFile());
155+
FileHistoryCache.writeHistoryTo(history, tempFile);
156+
// TODO: mock the repository
157+
// History deserialised = FileHistoryCache.readHistory(tempFile, repository);
158+
// assertEquals(history, deserialised);
158159
}
159160
}

opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexDatabaseTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ private void checkDataExistence(String fileName, boolean shouldExist) {
223223
assertFalse(historyGuru.hasAnnotationCacheForFile(file));
224224
}
225225
} else {
226-
cacheFile = TandemPath.join(fileName, ".gz");
226+
cacheFile = TandemPath.join(fileName, dirName.equals(IndexDatabase.XREF_DIR) ? ".gz" : "");
227227
File dataFile = new File(dataDir, cacheFile);
228228

229229
if (shouldExist) {

opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexerTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public void fileRemoved(String path) {
304304
if (path.equals(this.path)) {
305305
RuntimeEnvironment env = RuntimeEnvironment.getInstance();
306306
File f = new File(env.getDataRootPath(),
307-
TandemPath.join("historycache" + path, ".gz"));
307+
TandemPath.join("historycache" + path, ""));
308308
assertTrue(f.exists(), String.format("history cache file %s should be preserved", f));
309309
}
310310
removedFiles.add(path);
@@ -339,7 +339,7 @@ void testRemoveFileOnFileChange() throws Exception {
339339
Indexer.getInstance().prepareIndexer(env, true, true,
340340
null, List.of("mercurial"));
341341
File historyFile = new File(env.getDataRootPath(),
342-
TandemPath.join("historycache" + path, ".gz"));
342+
TandemPath.join("historycache" + path, ""));
343343
assertTrue(historyFile.exists(), String.format("history cache for %s has to exist", path));
344344

345345
// create index
Binary file not shown.

0 commit comments

Comments
 (0)