1 package com.trendmicro.grid.acl.ds.jpa;
2
3 import com.trendmicro.grid.acl.ds.FileProvider;
4 import com.trendmicro.grid.acl.ds.RemoteCacheable;
5 import com.trendmicro.grid.acl.ds.TagQueryExpression;
6 import com.trendmicro.grid.acl.ds.datatypes.SharedFileDetails;
7 import com.trendmicro.grid.acl.ds.datatypes.SharedFileInformation;
8 import com.trendmicro.grid.acl.ds.jpa.entities.JpaFileDetails;
9 import com.trendmicro.grid.acl.ds.jpa.entities.JpaFileIdentifier;
10 import com.trendmicro.grid.acl.ds.jpa.entities.JpaFileInformation;
11 import com.trendmicro.grid.acl.ds.jpa.tagquery.TagQueryProviderSelector;
12 import com.trendmicro.grid.acl.ds.jpa.util.FileQueryConfigurator;
13 import com.trendmicro.grid.acl.l0.datatypes.*;
14 import net.sf.tinyjee.util.Hex;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.springframework.stereotype.Repository;
18 import org.springframework.transaction.annotation.Transactional;
19
20 import javax.annotation.Resource;
21 import javax.persistence.*;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25
26 import static com.trendmicro.grid.acl.ds.jpa.util.JpaUtils.*;
27 import static net.sf.tinyjee.util.Assert.assertNotNull;
28 import static net.sf.tinyjee.util.Assert.assertTrue;
29
30
31
32
33
34
35
36 @Repository
37 @Transactional(readOnly = true)
38 public class JpaFileRepository implements FileProvider {
39
40 private static final Logger log = LoggerFactory.getLogger(JpaFileRepository.class);
41
42 private static int pageSize = 1000;
43
44 public static int getPageSize() {
45 return pageSize;
46 }
47
48 public static void setPageSize(int pageSize) {
49 JpaFileRepository.pageSize = pageSize;
50 }
51
52 static final Callback<Object[], FileIdentifier> bytesToFileIdentifierConverter = new Callback<Object[], FileIdentifier>() {
53 @Override
54 public FileIdentifier call(Object[] hashes) {
55 return new FileIdentifier(
56 (byte[]) hashes[hashes.length - 2],
57 (byte[]) hashes[hashes.length - 1]);
58 }
59 };
60
61 @PersistenceContext(unitName = "CoreDB")
62 EntityManager em;
63
64 @Resource
65 TagQueryProviderSelector providerSelector;
66
67 @Override
68 @RemoteCacheable
69 public Collection<Boolean> isFilesKnown(Collection<FileIdentifier> files) {
70 final FileQueryConfigurator<Integer> queryConfigurator = new FileQueryConfigurator<Integer>(em, Integer.class, "FileContents.IsFileKnown");
71
72 final Collection<Boolean> results = new ArrayList<Boolean>(files.size());
73 for (FileIdentifier file : files) {
74 List<Integer> resultList = queryConfigurator.getQuery(file).setMaxResults(1).getResultList();
75 results.add(!resultList.isEmpty() && resultList.get(0) == 1);
76 }
77
78 return results;
79 }
80
81 @Override
82 @RemoteCacheable
83 public Collection<Boolean> isFilesTaggedWith(Collection<FileIdentifier> files, String[] tags) {
84
85
86 final Collection<Boolean> results = new ArrayList<Boolean>(files.size());
87 for (SharedFileInformation info : getFileInformationList(files))
88 results.add(info == null || info.isUnknown() ? null : isTaggedWith(info, tags));
89
90 return results;
91 }
92
93 @Override
94 public FileIdentiferListPage getMatchingFiles(TagQueryExpression expression, Range range, int pageNumber) {
95 if (range != null && !(range instanceof DaysRange)) {
96 log.warn("TMACL-00830:Ignoring CompositeRange in JPA implementation. Such requests should get handled inside the Cache module.");
97 return null;
98 }
99
100 final TagQueryProvider provider = providerSelector.getSelectedProvider(em);
101 final Query filesQuery = provider.getMatchingFilesQuery(em, expression, (DaysRange) range);
102
103 applyPage(filesQuery, pageNumber, pageSize);
104
105 return toListPage(filesQuery, bytesToFileIdentifierConverter, new FileIdentiferListPage());
106 }
107
108 @Override
109 public Collection<FileIdentifier> getCanonicalIdentifiers(Collection<FileIdentifier> files) {
110 final FileQueryConfigurator<JpaFileIdentifier> queryConfigurator =
111 new FileQueryConfigurator<JpaFileIdentifier>(em, JpaFileIdentifier.class, "FileContents.SelectFileIdentifier");
112
113 final Collection<FileIdentifier> results = new ArrayList<FileIdentifier>(files.size());
114 for (FileIdentifier file : files) {
115 List<JpaFileIdentifier> resultList = queryConfigurator.getQuery(file).getResultList();
116 results.add(resultList.isEmpty() ? null : resultList.get(0));
117 }
118
119 return results;
120 }
121
122 @Override
123 @SuppressWarnings("unchecked")
124 @RemoteCacheable(comment = "Note: Don't use this method for JPA purposes, " +
125 "the returned entities will not be usable when returned from the remote cache.")
126 public Collection<SharedFileInformation> getFileInformationList(Collection<FileIdentifier> files) {
127 return (Collection) getJpaFileInformationList(files);
128 }
129
130
131
132
133
134
135
136
137 public Collection<JpaFileInformation> getJpaFileInformationList(Collection<FileIdentifier> files) {
138 final FileQueryConfigurator<JpaFileInformation> queryConfigurator =
139 new FileQueryConfigurator<JpaFileInformation>(em, JpaFileInformation.class, "FileContents.SelectFileInformation");
140
141 final Collection<JpaFileInformation> results = new ArrayList<JpaFileInformation>(files.size());
142 for (FileIdentifier file : files) {
143 List<JpaFileInformation> resultList = queryConfigurator.getQuery(file).getResultList();
144 results.add(resultList.isEmpty() ? null : resultList.get(0));
145 }
146
147 return results;
148 }
149
150 @Override
151 @SuppressWarnings("unchecked")
152 @RemoteCacheable(comment = "Note: Don't use this method for JPA purposes, " +
153 "the returned entities will not be usable when returned from the remote cache.")
154 public Collection<SharedFileDetails> getFileDetailsList(Collection<FileIdentifier> files) {
155 return (Collection) getJpaFileDetailsList(files);
156 }
157
158
159
160
161
162
163
164
165 public Collection<JpaFileDetails> getJpaFileDetailsList(Collection<FileIdentifier> files) {
166 final FileQueryConfigurator<JpaFileDetails> queryConfigurator = getDetailsQueryConfigurator();
167
168 final Collection<JpaFileDetails> results = new ArrayList<JpaFileDetails>(files.size());
169 for (FileIdentifier file : files) {
170 List<JpaFileDetails> resultList = queryConfigurator.getQuery(file).getResultList();
171 results.add(resultList.isEmpty() ? null : resultList.get(0));
172 }
173
174 return results;
175 }
176
177
178
179
180
181
182
183
184 public JpaFileDetails createFileDetails(FileDetails fileDetails) throws PersistenceException {
185 assertNotNull("fileDetails", fileDetails);
186 assertNotNull("fileDetails#getIdentifier", fileDetails.getIdentifier());
187 assertTrue(fileDetails.getIdentifier().isCanonical(), "fileDetails#getIdentifier#isCanonical");
188 assertNotNull("fileDetails#getInformation", fileDetails.getInformation());
189
190 JpaFileDetails details = new JpaFileDetails(
191 new JpaFileIdentifier(fileDetails.getIdentifier(), fileDetails.getMetadata()),
192 new JpaFileInformation(fileDetails.getInformation()), fileDetails.getMetadata());
193
194 em.persist(details);
195
196 return details;
197 }
198
199
200
201
202
203
204
205 public JpaFileDetails getFileDetails(FileIdentifier identifier) {
206 List<JpaFileDetails> r = getDetailsQueryConfigurator().getQuery(identifier).getResultList();
207 return r.isEmpty() ? null : r.get(0);
208 }
209
210
211
212
213
214
215
216 public Collection<JpaFileDetails> getReferences(Collection<? extends FileIdentifier> identifiers) {
217 final FileQueryConfigurator<Integer> configurator = getReferenceQueryConfigurator();
218 final Collection<JpaFileDetails> results = new ArrayList<JpaFileDetails>();
219
220 for (FileIdentifier identifier : identifiers)
221 results.add(getReference(configurator, identifier));
222
223 return results;
224 }
225
226
227
228
229
230
231
232 public JpaFileDetails getReference(FileIdentifier identifier) {
233 return getReference(getReferenceQueryConfigurator(), identifier);
234 }
235
236
237
238
239
240
241 public FileQueryConfigurator<JpaFileDetails> getDetailsQueryConfigurator() {
242 return new FileQueryConfigurator<JpaFileDetails>(em, JpaFileDetails.class, "FileContents.SelectFileDetails");
243 }
244
245
246
247
248
249
250 public FileQueryConfigurator<Integer> getReferenceQueryConfigurator() {
251 return new FileQueryConfigurator<Integer>(em, Integer.class, "FileContents.GetPrimaryKey");
252 }
253
254
255
256
257
258
259
260
261
262 public JpaFileDetails getReference(FileQueryConfigurator<Integer> primaryKeyQueryConfigurator, FileIdentifier identifier) {
263
264 final Integer primaryKey = getOrCreateFileContentPrimaryKey(primaryKeyQueryConfigurator, identifier);
265 return primaryKey == null ? null : em.getReference(JpaFileDetails.class, primaryKey);
266 }
267
268 Integer getOrCreateFileContentPrimaryKey(FileQueryConfigurator<Integer> primaryKeyQueryConfigurator, FileIdentifier identifier) {
269
270 final byte[] sha1 = identifier.getSHA1Hash();
271 final List<Integer> fileContentKeyResult = primaryKeyQueryConfigurator.getQuery(identifier).getResultList();
272
273 if (fileContentKeyResult.isEmpty()) {
274 final byte[] md5Hash = identifier.getMD5Hash();
275 if (md5Hash != null) {
276 final JpaFileDetails unknownFileContent = new JpaFileDetails(
277 new JpaFileIdentifier(sha1, md5Hash),
278 new JpaFileInformation(true), null);
279
280 if (log.isDebugEnabled()) log.debug("Creating new 'unknown' file content entry for file sha1={}", Hex.encode(sha1));
281
282 em.persist(unknownFileContent);
283 return unknownFileContent.getPrimaryKey();
284 }
285 return null;
286 } else {
287 return fileContentKeyResult.get(0);
288 }
289 }
290 }