1
22
23 package com.liferay.portal.dao.orm.common;
24
25 import com.liferay.portal.kernel.cache.CacheKVP;
26 import com.liferay.portal.kernel.cache.CacheRegistry;
27 import com.liferay.portal.kernel.cache.CacheRegistryItem;
28 import com.liferay.portal.kernel.cache.MultiVMPool;
29 import com.liferay.portal.kernel.cache.PortalCache;
30 import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
31 import com.liferay.portal.kernel.dao.orm.FinderCache;
32 import com.liferay.portal.kernel.dao.orm.FinderPath;
33 import com.liferay.portal.kernel.dao.orm.SessionFactory;
34 import com.liferay.portal.kernel.util.InitialThreadLocal;
35 import com.liferay.portal.kernel.util.StringBundler;
36 import com.liferay.portal.kernel.util.StringPool;
37 import com.liferay.portal.model.BaseModel;
38 import com.liferay.portal.util.PropsValues;
39
40 import java.io.Serializable;
41
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.concurrent.ConcurrentHashMap;
46
47 import org.apache.commons.collections.map.LRUMap;
48
49
54 public class FinderCacheImpl implements CacheRegistryItem, FinderCache {
55
56 public static final String CACHE_NAME = FinderCache.class.getName();
57
58 public void afterPropertiesSet() {
59 CacheRegistry.register(this);
60 }
61
62 public void clearCache() {
63 clearLocalCache();
64
65 PortalCache[] portalCaches = _portalCaches.values().toArray(
66 new PortalCache[_portalCaches.size()]);
67
68 for (PortalCache portalCache : portalCaches) {
69 portalCache.removeAll();
70 }
71 }
72
73 public void clearCache(String className) {
74 clearLocalCache();
75
76 PortalCache portalCache = _getPortalCache(className);
77
78 portalCache.removeAll();
79 }
80
81 public void clearLocalCache() {
82 if (_localCacheEnabled.get().booleanValue()) {
83 Map<String, Object> localCache = _localCache.get();
84
85 localCache.clear();
86 }
87 }
88
89 public String getRegistryName() {
90 return CACHE_NAME;
91 }
92
93 public Object getResult(
94 FinderPath finderPath, Object[] args, SessionFactory sessionFactory) {
95
96 if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
97 !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
98
99 return null;
100 }
101
102 Object primaryKey = null;
103
104 Map<String, Object> localCache = null;
105
106 String localCacheKey = null;
107
108 if (_localCacheEnabled.get().booleanValue()) {
109 localCache = _localCache.get();
110
111 localCacheKey = _encodeLocalCacheKey(
112 finderPath.getClassName(), finderPath.getMethodName(),
113 finderPath.getParams(), args);
114
115 primaryKey = localCache.get(localCacheKey);
116 }
117
118 if (primaryKey == null) {
119 PortalCache portalCache = _getPortalCache(
120 finderPath.getClassName());
121
122 String cacheKey = _encodeCacheKey(
123 finderPath.getMethodName(), finderPath.getParams(), args);
124
125 primaryKey = portalCache.get(cacheKey);
126
127 if (primaryKey != null) {
128 if (_localCacheEnabled.get().booleanValue()) {
129 localCache.put(localCacheKey, primaryKey);
130 }
131 }
132 }
133
134 if (primaryKey != null) {
135 return _primaryKeyToResult(finderPath, sessionFactory, primaryKey);
136 }
137 else {
138 return null;
139 }
140 }
141
142 public void invalidate() {
143 clearCache();
144 }
145
146 public void putResult(FinderPath finderPath, Object[] args, Object result) {
147 if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
148 !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive() ||
149 (result == null)) {
150
151 return;
152 }
153
154 Object primaryKey = _resultToPrimaryKey(result);
155
156 if (_localCacheEnabled.get().booleanValue()) {
157 Map<String, Object> localCache = _localCache.get();
158
159 String localCacheKey = _encodeLocalCacheKey(
160 finderPath.getClassName(), finderPath.getMethodName(),
161 finderPath.getParams(), args);
162
163 localCache.put(localCacheKey, primaryKey);
164 }
165
166 PortalCache portalCache = _getPortalCache(finderPath.getClassName());
167
168 String cacheKey = _encodeCacheKey(
169 finderPath.getMethodName(), finderPath.getParams(), args);
170
171 portalCache.put(cacheKey, primaryKey);
172 }
173
174 public void removeResult(FinderPath finderPath, Object[] args) {
175 if (!PropsValues.VALUE_OBJECT_FINDER_CACHE_ENABLED ||
176 !finderPath.isFinderCacheEnabled() || !CacheRegistry.isActive()) {
177
178 return;
179 }
180
181 if (_localCacheEnabled.get().booleanValue()) {
182 Map<String, Object> localCache = _localCache.get();
183
184 String localCacheKey = _encodeLocalCacheKey(
185 finderPath.getClassName(), finderPath.getMethodName(),
186 finderPath.getParams(), args);
187
188 localCache.remove(localCacheKey);
189 }
190
191 PortalCache portalCache = _getPortalCache(finderPath.getClassName());
192
193 String cacheKey = _encodeCacheKey(
194 finderPath.getMethodName(), finderPath.getParams(), args);
195
196 portalCache.remove(cacheKey);
197 }
198
199 public void setLocalCacheEnabled(boolean localCacheEnabled) {
200 if (_localCacheAvailable) {
201 _localCacheEnabled.set(Boolean.valueOf(localCacheEnabled));
202 }
203 }
204
205 public void setMultiVMPool(MultiVMPool multiVMPool) {
206 _multiVMPool = multiVMPool;
207 }
208
209 private String _encodeCacheKey(
210 String methodName, String[] params, Object[] args) {
211
212 StringBundler sb = new StringBundler(
213 (params.length + args.length) * 2 + 3);
214
215 sb.append(methodName);
216 sb.append(_PARAMS_SEPARATOR);
217
218 for (String param : params) {
219 sb.append(StringPool.PERIOD);
220 sb.append(param);
221 }
222
223 sb.append(_ARGS_SEPARATOR);
224
225 for (Object arg : args) {
226 sb.append(StringPool.PERIOD);
227 sb.append(String.valueOf(arg));
228 }
229
230 return sb.toString();
231 }
232
233 private String _encodeGroupKey(String className) {
234 return CACHE_NAME.concat(StringPool.PERIOD).concat(className);
235 }
236
237 private String _encodeLocalCacheKey(
238 String className, String methodName, String[] params, Object[] args) {
239
240 StringBundler sb = new StringBundler(
241 (params.length + args.length) * 2 + 5);
242
243 sb.append(className);
244 sb.append(StringPool.PERIOD);
245 sb.append(methodName);
246 sb.append(_PARAMS_SEPARATOR);
247
248 for (String param : params) {
249 sb.append(StringPool.PERIOD);
250 sb.append(param);
251 }
252
253 sb.append(_ARGS_SEPARATOR);
254
255 for (Object arg : args) {
256 sb.append(StringPool.PERIOD);
257 sb.append(String.valueOf(arg));
258 }
259
260 return sb.toString();
261 }
262
263 private PortalCache _getPortalCache(String className) {
264 String groupKey = _encodeGroupKey(className);
265
266 PortalCache portalCache = _portalCaches.get(groupKey);
267
268 if (portalCache == null) {
269 portalCache = _multiVMPool.getCache(
270 groupKey, PropsValues.VALUE_OBJECT_FINDER_BLOCKING_CACHE);
271
272 _portalCaches.put(groupKey, portalCache);
273 }
274
275 return portalCache;
276 }
277
278 private Object _primaryKeyToResult(
279 FinderPath finderPath, SessionFactory sessionFactory,
280 Object primaryKey) {
281
282 if (primaryKey instanceof CacheKVP) {
283 CacheKVP cacheKVP = (CacheKVP)primaryKey;
284
285 Class<?> modelClass = cacheKVP.getModelClass();
286 Serializable primaryKeyObj = cacheKVP.getPrimaryKeyObj();
287
288 return EntityCacheUtil.loadResult(
289 finderPath.isEntityCacheEnabled(), modelClass, primaryKeyObj,
290 sessionFactory);
291 }
292 else if (primaryKey instanceof List<?>) {
293 List<Object> cachedList = (List<Object>)primaryKey;
294
295 List<Object> list = new ArrayList<Object>(cachedList.size());
296
297 for (Object curPrimaryKey : cachedList) {
298 Object result = _primaryKeyToResult(
299 finderPath, sessionFactory, curPrimaryKey);
300
301 list.add(result);
302 }
303
304 return list;
305 }
306 else {
307 return primaryKey;
308 }
309 }
310
311 private Object _resultToPrimaryKey(Object result) {
312 if (result instanceof BaseModel<?>) {
313 BaseModel<?> model = (BaseModel<?>)result;
314
315 Class<?> modelClass = model.getClass();
316 Serializable primaryKeyObj = model.getPrimaryKeyObj();
317
318 return new CacheKVP(modelClass, primaryKeyObj);
319 }
320 else if (result instanceof List<?>) {
321 List<Object> list = (List<Object>)result;
322
323 List<Object> cachedList = new ArrayList<Object>(list.size());
324
325 for (Object curResult : list) {
326 Object primaryKey = _resultToPrimaryKey(curResult);
327
328 cachedList.add(primaryKey);
329 }
330
331 return cachedList;
332 }
333 else {
334 return result;
335 }
336 }
337
338 private static final String _ARGS_SEPARATOR = "_A_";
339
340 private static final String _PARAMS_SEPARATOR = "_P_";
341
342 private static ThreadLocal<LRUMap> _localCache;
343 private static boolean _localCacheAvailable;
344 private static ThreadLocal<Boolean> _localCacheEnabled =
345 new InitialThreadLocal<Boolean>(Boolean.FALSE);
346
347 static {
348 if (PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE > 0) {
349 _localCache = new InitialThreadLocal<LRUMap>(new LRUMap(
350 PropsValues.VALUE_OBJECT_FINDER_THREAD_LOCAL_CACHE_MAX_SIZE));
351 _localCacheAvailable = true;
352 }
353 }
354
355 private MultiVMPool _multiVMPool;
356 private Map<String, PortalCache> _portalCaches =
357 new ConcurrentHashMap<String, PortalCache>();
358
359 }