|
22 | 22 | */
|
23 | 23 | package org.opengrok.indexer.history;
|
24 | 24 |
|
| 25 | +import com.fasterxml.jackson.core.JsonToken; |
25 | 26 | import com.fasterxml.jackson.databind.ObjectMapper;
|
| 27 | +import com.fasterxml.jackson.dataformat.smile.SmileFactory; |
| 28 | +import com.fasterxml.jackson.dataformat.smile.SmileParser; |
26 | 29 | import com.fasterxml.jackson.dataformat.smile.databind.SmileMapper;
|
27 | 30 | import io.micrometer.core.instrument.Counter;
|
28 | 31 | import io.micrometer.core.instrument.MeterRegistry;
|
@@ -61,13 +64,54 @@ public void initialize() {
|
61 | 64 | }
|
62 | 65 |
|
63 | 66 | /**
|
64 |
| - * Read annotation from a file. |
| 67 | + * Read serialized {@link AnnotationData} from a file and create {@link Annotation} instance out of it. |
65 | 68 | */
|
66 | 69 | static Annotation readCache(File file) throws IOException {
|
67 | 70 | ObjectMapper mapper = new SmileMapper();
|
68 | 71 | return new Annotation(mapper.readValue(file, AnnotationData.class));
|
69 | 72 | }
|
70 | 73 |
|
| 74 | + /** |
| 75 | + * Retrieve revision from the cache for given file. This is done in a fashion that keeps I/O low. |
| 76 | + * Assumes that {@link AnnotationData#revision} is serialized in the cache file as the first member. |
| 77 | + * @param file source root file |
| 78 | + * @return revision from the cache file or {@code null} |
| 79 | + * @throws CacheException on error |
| 80 | + */ |
| 81 | + @Nullable |
| 82 | + String getRevision(File file) throws CacheException { |
| 83 | + File cacheFile; |
| 84 | + try { |
| 85 | + cacheFile = getCachedFile(file); |
| 86 | + } catch (CacheException e) { |
| 87 | + throw new CacheException("failed to get annotation cache file", e); |
| 88 | + } |
| 89 | + |
| 90 | + SmileFactory factory = new SmileFactory(); |
| 91 | + try (SmileParser parser = factory.createParser(cacheFile)) { |
| 92 | + parser.nextToken(); |
| 93 | + while (parser.getCurrentToken() != null) { |
| 94 | + if (parser.getCurrentToken().equals(JsonToken.FIELD_NAME)) { |
| 95 | + break; |
| 96 | + } |
| 97 | + parser.nextToken(); |
| 98 | + } |
| 99 | + |
| 100 | + if (parser.getCurrentName().equals("revision")) { |
| 101 | + parser.nextToken(); |
| 102 | + if (!parser.getCurrentToken().equals(JsonToken.VALUE_STRING)) { |
| 103 | + LOGGER.log(Level.WARNING, "failed to get revision from ''{0}''", cacheFile); |
| 104 | + return null; |
| 105 | + } |
| 106 | + return parser.getValueAsString(); |
| 107 | + } else { |
| 108 | + return null; |
| 109 | + } |
| 110 | + } catch (IOException e) { |
| 111 | + throw new CacheException(e); |
| 112 | + } |
| 113 | + } |
| 114 | + |
71 | 115 | @Override
|
72 | 116 | public String getCacheFileSuffix() {
|
73 | 117 | return "";
|
@@ -111,29 +155,26 @@ public Annotation get(File file, @Nullable String rev) throws CacheException {
|
111 | 155 | Annotation annotation = null;
|
112 | 156 | String latestRevision = LatestRevisionUtil.getLatestRevision(file);
|
113 | 157 | if (rev == null || (latestRevision != null && latestRevision.equals(rev))) {
|
114 |
| - // read from the cache |
115 |
| - annotation = readAnnotation(file); |
116 |
| - if (annotation != null) { |
117 |
| - /* |
118 |
| - * Double check that the cached annotation is not stale by comparing the stored revision |
119 |
| - * with revision to be fetched. |
120 |
| - * This should be more robust than the file time stamp based check performed by history cache, |
121 |
| - * at the expense of having to read from the annotation cache. |
122 |
| - */ |
123 |
| - final String storedRevision = annotation.getRevision(); |
124 |
| - /* |
125 |
| - * Even though store() does not allow to store annotation with null revision, the check |
126 |
| - * should be present to catch weird cases of someone not using the store() or general badness. |
127 |
| - */ |
128 |
| - if (storedRevision == null) { |
129 |
| - LOGGER.log(Level.FINER, "no stored revision in annotation cache for ''{0}''", file); |
130 |
| - annotation = null; |
131 |
| - } else if (!storedRevision.equals(latestRevision)) { |
132 |
| - LOGGER.log(Level.FINER, |
133 |
| - "stored revision {0} for ''{1}'' does not match latest revision {2}", |
134 |
| - new Object[]{storedRevision, file, rev}); |
135 |
| - annotation = null; |
136 |
| - } |
| 158 | + /* |
| 159 | + * Double check that the cached annotation is not stale by comparing the stored revision |
| 160 | + * with revision to be fetched. |
| 161 | + * This should be more robust than the file time stamp based check performed by history cache, |
| 162 | + * at the expense of having to read some content from the annotation cache. |
| 163 | + */ |
| 164 | + final String storedRevision = getRevision(file); |
| 165 | + /* |
| 166 | + * Even though store() does not allow to store annotation with null revision, the check |
| 167 | + * should be present to catch weird cases of someone not using the store() or general badness. |
| 168 | + */ |
| 169 | + if (storedRevision == null) { |
| 170 | + LOGGER.log(Level.FINER, "no stored revision in annotation cache for ''{0}''", file); |
| 171 | + } else if (!storedRevision.equals(latestRevision)) { |
| 172 | + LOGGER.log(Level.FINER, |
| 173 | + "stored revision {0} for ''{1}'' does not match latest revision {2}", |
| 174 | + new Object[]{storedRevision, file, rev}); |
| 175 | + } else { |
| 176 | + // read from the cache |
| 177 | + annotation = readAnnotation(file); |
137 | 178 | }
|
138 | 179 | }
|
139 | 180 |
|
|
0 commit comments