1   package com.trendmicro.grid.acl.ds.cache.aspects;
2   
3   import com.trendmicro.grid.acl.ds.cache.CacheAdapter;
4   import com.trendmicro.grid.acl.ds.cache.CacheSource;
5   import com.trendmicro.grid.acl.ds.cache.commands.GetFromCacheCommand;
6   import com.trendmicro.grid.acl.ds.cache.commands.GetFromPJPSourceCommand;
7   import com.trendmicro.grid.acl.ds.cache.commands.RemoveCommand;
8   import com.trendmicro.grid.acl.ds.cache.translation.FileAwareValueExistsTranslator;
9   import com.trendmicro.grid.acl.ds.cache.translation.FileDetailsToInformationTranslator;
10  import com.trendmicro.grid.acl.ds.cache.translation.ValueExistsTranslator;
11  import com.trendmicro.grid.acl.ds.datatypes.SharedFileDetails;
12  import com.trendmicro.grid.acl.ds.datatypes.SharedFileInformation;
13  import com.trendmicro.grid.acl.l0.datatypes.*;
14  import net.sf.tinyjee.cache.config.CacheRegion;
15  import org.aspectj.lang.ProceedingJoinPoint;
16  import org.aspectj.lang.annotation.AfterReturning;
17  import org.aspectj.lang.annotation.Around;
18  import org.aspectj.lang.annotation.Aspect;
19  import org.infinispan.Cache;
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  import org.springframework.stereotype.Service;
23  
24  import java.util.Collection;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.concurrent.Callable;
29  
30  import static com.trendmicro.grid.acl.ds.cache.CacheSettings.*;
31  
32  /**
33   * Implements an Aspect that wraps any implementation of FileProvider and adds
34   * caching capabilities to FileInformation and FileDetails related lookup methods.
35   *
36   * @author Juergen_Kellerer, 2011-02-19
37   */
38  @Aspect
39  @Service
40  public class FileDetailsCacheAspect extends AbstractCacheAspect {
41  
42  	private static final Logger log = LoggerFactory.getLogger(FileDetailsCacheAspect.class);
43  
44  	@CacheRegion(name = CACHE_REGION_FILE_DETAILS)
45  	Cache<FileIdentifier, SharedFileDetails> cachedFileDetails;
46  	@CacheRegion(name = CACHE_REGION_FILE_INFORMATION)
47  	Cache<FileIdentifier, SharedFileInformation> cachedFileInformation;
48  
49  	@CacheRegion(name = CACHE_REGION_UNKNOWN_FILE_IDENTIFIERS)
50  	Cache<FileIdentifier, Boolean> unknownFileIdentifiersCache;
51  
52  	private CacheAdapter<FileIdentifier, SharedFileDetails> detailsCacheAdapter;
53  	private CacheSource<FileIdentifier, SharedFileInformation> informationFromDetails;
54  	private CacheAdapter<FileIdentifier, SharedFileInformation> informationCacheAdapter;
55  	private CacheSource<FileIdentifier, Boolean>[] fileExistsSources;
56  
57  	private CacheAdapter<FileIdentifier, Boolean> unknownFileIdentifiers;
58  
59  	/**
60  	 * Wraps around method calls of isFilesKnown() and asks caches for answers before delegating to the db.
61  	 *
62  	 * @param pjp   The ProceedingJoinPoint used to call the wrapped method.
63  	 * @param files the files to check.
64  	 * @return a collection that contains the results of the requested files.
65  	 * @throws Exception In case of the cache operations failed.
66  	 */
67  	@Around(value = "execution(* com.trendmicro.grid.acl.ds.FileProvider.isFilesKnown(..)) && " +
68  			"args(files) && @annotation(com.trendmicro.grid.acl.ds.RemoteCacheable)", argNames = "pjp,files")
69  	public Collection<Boolean> isFilesKnown(final ProceedingJoinPoint pjp, Collection<FileIdentifier> files) throws Exception {
70  
71  		return call(new GetFromCacheCommand<FileIdentifier, Boolean>(files, fileExistsSources) {
72  			@Override
73  			protected Callable<Map<FileIdentifier, Boolean>> createGetFromSourceCommand(Collection<FileIdentifier> keys) {
74  				return new GetFromPJPSourceCommand<FileIdentifier, Boolean>(getCacheStatistics().getFetchedKeyCount(), keys, pjp);
75  			}
76  		});
77  	}
78  
79  	/**
80  	 * Wraps around method calls of getFileInformationList() and asks caches for answers before delegating to the db.
81  	 *
82  	 * @param pjp   The ProceedingJoinPoint used to call the wrapped method.
83  	 * @param files The files to return the information list for.
84  	 * @return the file information list for the given file. The list may contain 'null'
85  	 *         entries if the corresponding file was unknown.
86  	 * @throws Exception In case of the cache operations failed.
87  	 */
88  	@Around(value = "execution(* com.trendmicro.grid.acl.ds.FileProvider.getFileInformationList(..)) && " +
89  			"args(files) && @annotation(com.trendmicro.grid.acl.ds.RemoteCacheable)", argNames = "pjp,files")
90  	public Collection<SharedFileInformation> getFileInformationList(
91  			final ProceedingJoinPoint pjp, final Collection<FileIdentifier> files) throws Exception {
92  
93  		return call(new GetFromCacheCommand<FileIdentifier, SharedFileInformation>(files, informationCacheAdapter, informationFromDetails) {
94  			@Override
95  			protected Callable<Map<FileIdentifier, SharedFileInformation>> createGetFromSourceCommand(Collection<FileIdentifier> keys) {
96  				return new GetFromPJPSourceCommand<FileIdentifier, SharedFileInformation>(getCacheStatistics().getFetchedKeyCount(), keys, pjp);
97  			}
98  		}.useDestination(informationCacheAdapter, cacheWriteMode).useUnknownKeysLookup(unknownFileIdentifiers, cacheWriteMode));
99  	}
100 
101 	/**
102 	 * Wraps around method calls of getFileDetailsList() and asks caches for answers before delegating to the db.
103 	 *
104 	 * @param pjp   The ProceedingJoinPoint used to call the wrapped method.
105 	 * @param files The files to return the details list for.
106 	 * @return the file details list for the given file. The list may contain 'null'
107 	 *         entries if the corresponding file was unknown.
108 	 * @throws Exception In case of the cache operations failed.
109 	 */
110 	@Around(value = "execution(* com.trendmicro.grid.acl.ds.FileProvider.getFileDetailsList(..)) && " +
111 			"args(files) && @annotation(com.trendmicro.grid.acl.ds.RemoteCacheable)", argNames = "pjp,files")
112 	public Collection<SharedFileDetails> getFileDetailsList(
113 			final ProceedingJoinPoint pjp, final Collection<FileIdentifier> files) throws Exception {
114 
115 		return call(new GetFromCacheCommand<FileIdentifier, SharedFileDetails>(files, detailsCacheAdapter) {
116 			@Override
117 			protected Callable<Map<FileIdentifier, SharedFileDetails>> createGetFromSourceCommand(Collection<FileIdentifier> keys) {
118 				return new GetFromPJPSourceCommand<FileIdentifier, SharedFileDetails>(getCacheStatistics().getFetchedKeyCount(), keys, pjp);
119 			}
120 		}.useDestination(detailsCacheAdapter, cacheWriteMode).useUnknownKeysLookup(unknownFileIdentifiers, cacheWriteMode));
121 	}
122 
123 	/**
124 	 * Fires after method calls to receive(..) and invalidates caches on all newly stored entries.
125 	 *
126 	 * @param dataSets the stored datasets.
127 	 * @throws Exception In case of the cache operations failed.
128 	 */
129 	@AfterReturning(value = "execution(* com.trendmicro.grid.acl.ds.ProcessingResultReceiver.receive(..)) && " +
130 			"args(dataSets)", argNames = "dataSets")
131 	public void invalidateCachesAfterReceivingUpdates(final Collection<ProcessPackageDataSet> dataSets) throws Exception {
132 
133 		final List<FileIdentifier> keysToRemove = new LinkedList<FileIdentifier>();
134 		for (ProcessPackageDataSet dataSet : dataSets) {
135 			final PackageDetails processedPackage = dataSet.getProcessedPackage();
136 			final FileMetadata fm = processedPackage.getFileMetadata();
137 			if (fm != null) {
138 				keysToRemove.addAll(fm.getIdentifier().getVariants());
139 
140 				if (log.isTraceEnabled()) {
141 					log.trace("Invalidated cached file entries for package '{}', identifier '{}'",
142 							processedPackage.getPackageInformation().getName(), fm.getIdentifier());
143 				}
144 			}
145 
146 			for (PackageMember member : dataSet.getPackageMember()) {
147 				keysToRemove.addAll(member.getIdentifier().extractIdentifier().getVariants());
148 
149 				if (log.isTraceEnabled()) log.trace("Invalidated cached file entries for member '{}'", member.getIdentifier());
150 			}
151 		}
152 
153 		call(new RemoveCommand<FileIdentifier, Object>(keysToRemove,
154 				detailsCacheAdapter, informationCacheAdapter, unknownFileIdentifiers).asBatchCommand());
155 	}
156 
157 	/**
158 	 * Is used to set the cache region for file details (called by spring).
159 	 *
160 	 * @param cachedFileDetails the cache for file details.
161 	 */
162 	public void setCachedFileDetails(Cache<FileIdentifier, SharedFileDetails> cachedFileDetails) {
163 		this.cachedFileDetails = cachedFileDetails;
164 		detailsCacheAdapter = new CacheAdapter<FileIdentifier, SharedFileDetails>(cachedFileDetails, sync);
165 
166 		FileDetailsToInformationTranslator<FileIdentifier> t = new FileDetailsToInformationTranslator<FileIdentifier>();
167 		informationFromDetails = t.newSource(detailsCacheAdapter);
168 
169 		updateFileExistsSources();
170 	}
171 
172 	/**
173 	 * Is used to set the cache region for file information (called by spring).
174 	 *
175 	 * @param cachedFileInformation the cache for file information.
176 	 */
177 	public void setCachedFileInformation(Cache<FileIdentifier, SharedFileInformation> cachedFileInformation) {
178 		this.cachedFileInformation = cachedFileInformation;
179 		informationCacheAdapter = new CacheAdapter<FileIdentifier, SharedFileInformation>(cachedFileInformation, sync);
180 
181 		updateFileExistsSources();
182 	}
183 
184 	public void setUnknownFileIdentifiersCache(Cache<FileIdentifier, Boolean> unknownFileIdentifiersCache) {
185 		this.unknownFileIdentifiersCache = unknownFileIdentifiersCache;
186 		unknownFileIdentifiers = new CacheAdapter<FileIdentifier, Boolean>(unknownFileIdentifiersCache, sync);
187 
188 		updateFileExistsSources();
189 	}
190 
191 	private void updateFileExistsSources() {
192 		if (detailsCacheAdapter != null && informationCacheAdapter != null) {
193 			ValueExistsTranslator<FileIdentifier> t = new FileAwareValueExistsTranslator<FileIdentifier>();
194 
195 			@SuppressWarnings("unchecked")
196 			final CacheSource<FileIdentifier, Boolean>[] sources = new CacheSource[]{
197 					t.newSource(detailsCacheAdapter), t.newSource(informationCacheAdapter), unknownFileIdentifiers};
198 
199 			fileExistsSources = sources;
200 		}
201 	}
202 }