1 package com.trendmicro.grid.acl.ds.jpa;
2
3 import com.trendmicro.grid.acl.WellKnownTags;
4 import com.trendmicro.grid.acl.ds.jpa.entities.*;
5 import com.trendmicro.grid.acl.l0.datatypes.*;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import javax.persistence.Query;
10 import java.util.*;
11
12 import static com.trendmicro.grid.acl.ds.jpa.ReceivedPreparedPackagesHandler.PreparedPackage;
13 import static com.trendmicro.grid.acl.ds.jpa.StorageOptions.PATH_DELIMITERS;
14 import static com.trendmicro.grid.acl.ds.jpa.entities.JpaNamedFileIdentifierHistory.HistoryType.removed;
15 import static com.trendmicro.grid.acl.ds.jpa.entities.JpaNamedFileIdentifierHistory.HistoryType.renamed;
16
17
18
19
20
21
22
23 public class ReceivedDataSetsHandler extends AbstractReceivedFileContentsHandler<Void> {
24
25 private static final Logger log = LoggerFactory.getLogger(ReceivedDataSetsHandler.class);
26
27 private final JpaPackageRepository packageRepository;
28 private final JpaSourceRepository sourceRepository;
29
30 private final ReceivedPreparedPackagesHandler packagesHandler;
31 private final ReceivedSourcesHandler sourcesHandler;
32
33
34
35
36
37
38
39
40
41 public ReceivedDataSetsHandler(JpaFileRepository fileRepository,
42 JpaPackageRepository packageRepository,
43 ReceivedPreparedPackagesHandler packagesHandler,
44 ReceivedSourcesHandler sourcesHandler) {
45 super(fileRepository);
46
47 this.packageRepository = packageRepository;
48 this.packagesHandler = packagesHandler;
49 this.sourcesHandler = sourcesHandler;
50 sourceRepository = sourcesHandler.getSourceRepository();
51 }
52
53
54
55
56 @Override
57 protected Void handle(StorageContext context) {
58 final Collection<PreparedPackage> preparedPackages = packagesHandler.handle(context);
59 final Iterator<ProcessPackageDataSet> dataSets = context.receivedDataSets.iterator();
60 final Iterator<List<JpaSource>> sourceReferences = sourcesHandler.handle(context).iterator();
61
62 for (PreparedPackage preparedPackage : preparedPackages) {
63 final List<JpaSource> sources = sourceReferences.next();
64 final ProcessPackageDataSet dataSet = dataSets.next();
65 if (preparedPackage != null)
66 handlePreparedPackage(context, preparedPackage, sources, dataSet);
67 }
68
69 return null;
70 }
71
72
73
74
75
76
77
78
79
80 void handlePreparedPackage(final StorageContext context,
81 final PreparedPackage preparedPackage,
82 final List<JpaSource> sourceReferences,
83 final ProcessPackageDataSet dataSet) {
84
85
86 final Collection<JpaFileDetails> fileRefs = createOrUpdateFileReferences(context, dataSet.getPackageMember());
87 final boolean revisionNeedsIncrement = synchronizeMembers(context, preparedPackage, fileRefs, dataSet);
88
89
90 fileRefs.add(preparedPackage.getFileReference());
91 sourceRepository.referenceFilesFromSources(fileRefs, sourceReferences);
92
93
94 linkWithRootPackage(context, preparedPackage);
95
96 preparedPackage.applyFinalPackageUpdates(revisionNeedsIncrement);
97
98
99
100 context.em.flush();
101 }
102
103
104
105
106
107
108
109
110
111
112 boolean synchronizeMembers(final StorageContext context,
113 final PreparedPackage preparedPackage,
114 final Collection<JpaFileDetails> fileRefs,
115 final ProcessPackageDataSet dataSet) {
116
117
118 final Iterator<JpaFileDetails> fileRefsIterator = fileRefs.iterator();
119
120
121 final JpaPackageDetails packageRef = preparedPackage.getReference();
122 boolean historyEntriesCreated = false;
123 final int revision = packageRef.getRevision();
124
125 final Map<NamedFileIdentifier, JpaNamedFileIdentifier> namedFileIdentifierMap =
126 packageRepository.getFilesContainedInPackage(packageRef);
127 final Map<FileIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier>> fileIdentifierMap =
128 buildIdentifierOnlyMap(namedFileIdentifierMap);
129
130 final int memberCount = dataSet.getPackageMember().size();
131 final Set<JpaNamedFileIdentifier> persistedOrChangedRefs = new HashSet<JpaNamedFileIdentifier>(memberCount);
132 final IdentityHashMap<Object, Object> ignoredRefs = new IdentityHashMap<Object, Object>(memberCount);
133
134
135 Query fnUpdateQuery = null;
136 for (PackageMember member : dataSet.getPackageMember()) {
137 final JpaFileDetails fileRef = fileRefsIterator.next();
138 final NamedFileIdentifier namedIdentifier = member.getIdentifier();
139 final String fileName = namedIdentifier.getFileName();
140
141 JpaNamedFileIdentifier ref = namedFileIdentifierMap.get(namedIdentifier);
142 if (ref == null) {
143 final FileIdentifier key = new FileIdentifier(namedIdentifier.getSHA1Hash());
144 ref = findRenamedReference(namedIdentifier, fileIdentifierMap.get(key));
145
146 if (ref == null) {
147
148 ref = new JpaNamedFileIdentifier(fileRef, packageRef, fileName);
149
150 if (persistedOrChangedRefs.contains(ref)) {
151 log.warn("Ignoring a duplicate file reference for file '{}', " +
152 "this is very likely invalid data in the received data-set.", fileName);
153 continue;
154 }
155
156 if (log.isTraceEnabled())
157 log.trace("Creating new package file reference for file '{}'", fileName);
158
159 context.em.persist(ref);
160 mapIdentifier(namedIdentifier, ref, fileIdentifierMap);
161 persistedOrChangedRefs.add(ref);
162
163 } else {
164
165 if (persistedOrChangedRefs.contains(ref)) {
166 log.warn("Ignoring a duplicate request to rename file '{}' => '{}', " +
167 "this is very likely invalid data in the received data-set.",
168 ref.getReference().getFileName(), fileName);
169 continue;
170 }
171 persistedOrChangedRefs.add(ref);
172
173 ignoredRefs.put(ref, ref);
174 JpaNamedFileIdentifierHistory history = new JpaNamedFileIdentifierHistory(
175 fileRef, packageRef, ref.getReference().getFileName(), renamed, revision);
176
177
178 if (context.enableHistoryRecording) {
179 if (log.isTraceEnabled()) {
180 log.trace("Storing history entry for renamed file '{}' => '{}'",
181 ref.getReference().getFileName(), fileName);
182 }
183 context.em.persist(history);
184 } else {
185 if (log.isTraceEnabled())
186 log.trace("enableHistoryRecording is set to {}. Disregarding package-file history entry: {}", context.enableHistoryRecording, history);
187 }
188
189 historyEntriesCreated = true;
190
191
192 if (fnUpdateQuery == null)
193 fnUpdateQuery = context.em.createNamedQuery("NamedFileIdentifier.UpdateFileNameById");
194 int updateCount = fnUpdateQuery.setParameter("fileName", fileName).
195 setParameter("id", ref.getReference()).executeUpdate();
196 if (updateCount != 1) {
197
198 throw new IllegalStateException("Failed to rename file '" +
199 ref.getReference().getFileName() + "' to '" + fileName + "' for package '" +
200 packageRef.getPackageInformation().getName() + "'");
201 }
202 }
203 } else
204 ignoredRefs.put(ref, ref);
205 }
206
207
208 PackageInformation info = dataSet.getProcessedPackage().getPackageInformation();
209 boolean packageIsOversized = info.containsTag(WellKnownTags.oversized.name());
210 if (!packageIsOversized) {
211 for (JpaNamedFileIdentifier ref : namedFileIdentifierMap.values()) {
212 if (ignoredRefs.containsKey(ref))
213 continue;
214
215 JpaNamedFileIdentifierHistory history = new JpaNamedFileIdentifierHistory(
216 ref.getReference().getFileDetails(), packageRef, ref.getReference().getFileName(),
217 removed, revision);
218 if (log.isTraceEnabled())
219
220
221
222 if (context.enableHistoryRecording) {
223 if (log.isTraceEnabled())
224 log.trace("Storing history entry for the removed file '{}'", ref.getReference().getFileName());
225 context.em.persist(history);
226 } else {
227 if (log.isTraceEnabled())
228 log.trace("enableHistoryRecording is set to {}. Disregarding package-file history entry: {}", context.enableHistoryRecording, history);
229 }
230
231 context.em.remove(ref);
232 historyEntriesCreated = true;
233 }
234 }
235
236 return historyEntriesCreated;
237 }
238
239
240
241
242
243
244
245
246
247
248 private JpaNamedFileIdentifier findRenamedReference(NamedFileIdentifier newIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier> map) {
249
250 if (map == null || map.isEmpty()) return null;
251
252 final String newFileName = newIdentifier.getFileName();
253 for (Map.Entry<NamedFileIdentifier, JpaNamedFileIdentifier> entry : map.entrySet()) {
254 final NamedFileIdentifier identifier = entry.getKey();
255
256 if (!identifier.identifierEquals(newIdentifier)) continue;
257
258 boolean pathIsSameButCaseChange = !newFileName.equals(identifier.getFileName()) && newFileName.equalsIgnoreCase(identifier.getFileName());
259 if (checkOnlyNameDiffers(newFileName, identifier.getFileName()) || pathIsSameButCaseChange)
260 return entry.getValue();
261 }
262
263 return null;
264 }
265
266
267
268
269
270
271
272
273 boolean checkOnlyNameDiffers(String pathA, String pathB) {
274
275 final StringTokenizer ta = new StringTokenizer(pathA == null ? "" : pathA, PATH_DELIMITERS),
276 tb = new StringTokenizer(pathB == null ? "" : pathB, PATH_DELIMITERS);
277
278 while (ta.hasMoreTokens() && tb.hasMoreTokens()) {
279 if (!ta.nextToken().equals(tb.nextToken()))
280 return !ta.hasMoreTokens() && !tb.hasMoreTokens();
281 }
282
283 return false;
284 }
285
286
287
288
289
290
291
292 private Map<FileIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier>> buildIdentifierOnlyMap(
293 Map<NamedFileIdentifier, JpaNamedFileIdentifier> namedFileIdentifierMap) {
294
295 Map<FileIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier>> map =
296 new HashMap<FileIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier>>(
297 namedFileIdentifierMap.size());
298
299 for (Map.Entry<NamedFileIdentifier, JpaNamedFileIdentifier> entry : namedFileIdentifierMap.entrySet())
300 mapIdentifier(entry.getKey(), entry.getValue(), map);
301
302 return map;
303 }
304
305 private void mapIdentifier(NamedFileIdentifier namedIdentifier, JpaNamedFileIdentifier reference,
306 Map<FileIdentifier, Map<NamedFileIdentifier, JpaNamedFileIdentifier>> map) {
307 final FileIdentifier key = new FileIdentifier(namedIdentifier.getSHA1Hash());
308
309 Map<NamedFileIdentifier, JpaNamedFileIdentifier> identifierMap = map.get(key);
310 if (identifierMap == null) {
311
312
313 map.put(key, Collections.singletonMap(namedIdentifier, reference));
314 return;
315 } else if (identifierMap.size() == 1)
316 map.put(key, identifierMap = new LinkedHashMap<NamedFileIdentifier, JpaNamedFileIdentifier>(identifierMap));
317
318 identifierMap.put(namedIdentifier, reference);
319 }
320
321
322
323
324
325
326
327 void linkWithRootPackage(final StorageContext context, final PreparedPackage preparedPackage) {
328 final JpaPackageDetails rootPackageRef = context.getRootPackageReference(packageRepository);
329 final String remoteFileName = preparedPackage.getRemoteFileName();
330
331 if (rootPackageRef != null) {
332 final JpaFileDetails fileReference = preparedPackage.getFileReference();
333
334 final List r = context.em.createNamedQuery("NamedFileIdentifier.SelectEntryExists").
335 setParameter("package", rootPackageRef).
336 setParameter("file", fileReference).
337 setParameter("fileName", remoteFileName).setMaxResults(1).getResultList();
338
339 if (r.isEmpty()) {
340 JpaNamedFileIdentifier nfi = new JpaNamedFileIdentifier(fileReference, rootPackageRef, remoteFileName);
341 context.em.persist(nfi);
342 }
343 } else {
344 if (log.isTraceEnabled())
345 log.trace("Not inserting linkage to ROOT package with filename '{}', " +
346 "no ROOT package exists inside the PACKAGES table.", remoteFileName);
347 }
348 }
349 }