17
17
package net.sf.ehcache.store;
19
import net.sf.ehcache.CacheException;
20
19
import net.sf.ehcache.Ehcache;
21
20
import net.sf.ehcache.Element;
22
21
import net.sf.ehcache.config.CacheConfiguration;
23
22
import net.sf.ehcache.pool.Pool;
24
23
import net.sf.ehcache.search.Attribute;
25
import net.sf.ehcache.search.Result;
26
24
import net.sf.ehcache.search.Results;
27
25
import net.sf.ehcache.search.aggregator.AggregatorInstance;
28
26
import net.sf.ehcache.search.attribute.AttributeExtractor;
27
import net.sf.ehcache.search.attribute.DynamicAttributesExtractor;
29
28
import net.sf.ehcache.search.expression.Criteria;
30
29
import net.sf.ehcache.search.impl.AggregateOnlyResult;
31
30
import net.sf.ehcache.search.impl.BaseResult;
31
import net.sf.ehcache.search.impl.GroupedResultImpl;
32
32
import net.sf.ehcache.search.impl.OrderComparator;
33
33
import net.sf.ehcache.search.impl.ResultImpl;
34
34
import net.sf.ehcache.search.impl.ResultsImpl;
35
import net.sf.ehcache.transaction.SoftLock;
35
import net.sf.ehcache.search.impl.SearchManager;
36
import net.sf.ehcache.transaction.SoftLockID;
38
import static net.sf.ehcache.search.expression.BaseCriteria.getExtractor;
37
40
import java.util.ArrayList;
41
import java.util.Collection;
38
42
import java.util.Collections;
39
43
import java.util.HashMap;
44
import java.util.HashSet;
45
import java.util.LinkedList;
40
46
import java.util.List;
41
47
import java.util.Map;
42
import java.util.concurrent.ConcurrentHashMap;
49
import java.util.concurrent.locks.Lock;
45
52
* A memory-only store with support for all caching features.
47
54
* @author Ludovic Orban
49
public final class MemoryOnlyStore extends FrontEndCacheTier<NullStore, MemoryStore> {
56
public class MemoryOnlyStore extends FrontEndCacheTier<NullStore, MemoryStore> {
51
58
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
53
private final Map<String, AttributeExtractor> attributeExtractors = new ConcurrentHashMap<String, AttributeExtractor>();
54
private final Map<String, Attribute> searchAttributes = new ConcurrentHashMap<String, Attribute>();
57
private MemoryOnlyStore(CacheConfiguration cacheConfiguration, NullStore cache, MemoryStore authority) {
58
super(cache, authority, cacheConfiguration.getCopyStrategy(), cacheConfiguration.isCopyOnWrite(), cacheConfiguration.isCopyOnRead());
61
* Create a MemoryOnlyStore
63
* @param cacheConfiguration the cache configuration
64
* @param authority the memory store
66
protected MemoryOnlyStore(CacheConfiguration cacheConfiguration, MemoryStore authority, SearchManager searchManager) {
67
super(NullStore.create(), authority, cacheConfiguration.getCopyStrategy(), searchManager,
68
cacheConfiguration.isCopyOnWrite(), cacheConfiguration.isCopyOnRead());
62
* Create an instance of MemoryStore
72
* Create an instance of MemoryOnlyStore
63
73
* @param cache the cache
64
74
* @param onHeapPool the on heap pool
65
* @return an instance of MemoryStore
75
* @return an instance of MemoryOnlyStore
67
77
public static Store create(Ehcache cache, Pool onHeapPool) {
68
final NullStore nullStore = NullStore.create();
69
78
final MemoryStore memoryStore = NotifyingMemoryStore.create(cache, onHeapPool);
70
return new MemoryOnlyStore(cache.getCacheConfiguration(), nullStore, memoryStore);
77
public void setAttributeExtractors(Map<String, AttributeExtractor> extractors) {
78
this.attributeExtractors.putAll(extractors);
80
for (String name : extractors.keySet()) {
81
searchAttributes.put(name, new Attribute(name));
89
public Results executeQuery(StoreQuery query) {
90
Criteria c = query.getCriteria();
92
List<AggregatorInstance<?>> aggregators = query.getAggregatorInstances();
95
boolean includeResults = query.requestsKeys() || query.requestsValues() || !query.requestedAttributes().isEmpty();
97
ArrayList<Result> results = new ArrayList<Result>();
99
boolean hasOrder = !query.getOrdering().isEmpty();
101
boolean anyMatches = false;
103
for (Element element : authority.elementSet()) {
104
if (!hasOrder && query.maxResults() >= 0 && results.size() == query.maxResults()) {
107
if (element.getObjectValue() instanceof SoftLock) {
111
if (c.execute(element, attributeExtractors)) {
114
if (includeResults) {
115
final Map<String, Object> attributes;
116
if (query.requestedAttributes().isEmpty()) {
117
attributes = Collections.emptyMap();
119
attributes = new HashMap<String, Object>();
120
for (Attribute attribute : query.requestedAttributes()) {
121
String name = attribute.getAttributeName();
122
attributes.put(name, attributeExtractors.get(name).attributeFor(element, name));
126
final Object[] sortAttributes;
127
List<StoreQuery.Ordering> orderings = query.getOrdering();
128
if (orderings.isEmpty()) {
129
sortAttributes = EMPTY_OBJECT_ARRAY;
131
sortAttributes = new Object[orderings.size()];
132
for (int i = 0; i < sortAttributes.length; i++) {
133
String name = orderings.get(i).getAttribute().getAttributeName();
134
sortAttributes[i] = attributeExtractors.get(name).attributeFor(element, name);
139
results.add(new ResultImpl(element.getObjectKey(), element.getObjectValue(), query, attributes, sortAttributes));
142
for (AggregatorInstance<?> aggregator : aggregators) {
143
Attribute<?> attribute = aggregator.getAttribute();
144
if (attribute == null) {
145
aggregator.accept(null);
147
Object val = attributeExtractors.get(attribute.getAttributeName()).attributeFor(element,
148
attribute.getAttributeName());
149
aggregator.accept(val);
156
Collections.sort(results, new OrderComparator(query.getOrdering()));
158
// trim results to max length if necessary
159
int max = query.maxResults();
160
if (max >= 0 && (results.size() > max)) {
161
results.subList(max, results.size()).clear();
162
results.trimToSize();
167
List<Object> aggregateResults = aggregators.isEmpty() ? Collections.emptyList() : new ArrayList<Object>();
168
for (AggregatorInstance<?> aggregator : aggregators) {
169
aggregateResults.add(aggregator.aggregateResult());
172
if (anyMatches && !includeResults && !aggregateResults.isEmpty()) {
173
// add one row in the results if the only thing included was aggregators and anything matched
174
results.add(new AggregateOnlyResult(query));
178
if (!aggregateResults.isEmpty()) {
179
for (Result result : results) {
181
((BaseResult)result).setAggregateResults(aggregateResults);
185
return new ResultsImpl(results, query.requestsKeys(), query.requestsValues(),
186
!query.requestedAttributes().isEmpty(), anyMatches && !aggregateResults.isEmpty());
193
public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
194
return searchAttributes.get(attributeName);
79
final BruteForceSearchManager searchManager = new BruteForceSearchManager();
81
MemoryOnlyStore memoryOnlyStore = new MemoryOnlyStore(cache.getCacheConfiguration(), memoryStore, searchManager);
82
searchManager.setMemoryStore(memoryOnlyStore);
83
return memoryOnlyStore;
87
* Get the underyling memory store element set
91
Collection<Element> elementSet() {
92
return authority.elementSet();
99
public Element get(Object key) {
104
Lock lock = getLockFor(key).readLock();
107
return copyElementForReadIfNeeded(authority.get(key));
117
public Element getQuiet(Object key) {
122
Lock lock = getLockFor(key).readLock();
125
return copyElementForReadIfNeeded(authority.getQuiet(key));
135
public void setInMemoryEvictionPolicy(final Policy policy) {
136
authority.setInMemoryEvictionPolicy(policy);
143
public Policy getInMemoryEvictionPolicy() {
144
return authority.getInMemoryEvictionPolicy();
200
150
public Object getMBean() {
156
* Brute force search implementation
160
protected static class BruteForceSearchManager implements SearchManager {
162
private volatile MemoryOnlyStore memoryStore;
165
* Create a BruteForceSearchManager
167
public BruteForceSearchManager() {
172
* set the memory store
176
public void setMemoryStore(MemoryOnlyStore memoryStore) {
177
this.memoryStore = memoryStore;
181
public void put(String cacheName, int segmentId, Element element, Map<String, AttributeExtractor> extractors,
182
DynamicAttributesExtractor dynamicIndexer) {
183
throw new UnsupportedOperationException();
187
public Results executeQuery(String cacheName, StoreQuery query, Map<String, AttributeExtractor> extractors) {
188
Criteria c = query.getCriteria();
190
List<AggregatorInstance<?>> aggregators = query.getAggregatorInstances();
192
final Set<Attribute<?>> groupByAttributes = query.groupByAttributes();
193
final boolean isGroupBy = !groupByAttributes.isEmpty();
194
boolean includeResults = query.requestsKeys() || query.requestsValues() || !query.requestedAttributes().isEmpty() || isGroupBy;
196
boolean hasOrder = !query.getOrdering().isEmpty();
198
final Map<Set<?>, BaseResult> groupByResults = new HashMap<Set<?>, BaseResult>();
199
final Map<Set, List<AggregatorInstance<?>>> groupByAggregators = new HashMap<Set, List<AggregatorInstance<?>>>();
200
final int maxResults = query.maxResults();
202
Collection<Element> matches = new LinkedList<Element>();
204
for (Element element : memoryStore.elementSet()) {
205
element = memoryStore.copyElementForReadIfNeeded(element);
207
if (element.getObjectValue() instanceof SoftLockID) {
211
if (c.execute(element, extractors)) {
212
if (!isGroupBy && !hasOrder && query.maxResults() >= 0 && matches.size() == query.maxResults()) {
216
matches.add(element);
220
Collection<BaseResult> results = isGroupBy ? groupByResults.values() : new ArrayList<BaseResult>();
222
boolean anyMatches = !matches.isEmpty();
223
for (Element element : matches) {
224
if (includeResults) {
225
final Map<String, Object> attributes = getAttributeValues(query.requestedAttributes(), extractors, element);
226
final Object[] sortAttributes = getSortAttributes(query, extractors, element);
229
results.add(new ResultImpl(element.getObjectKey(), element.getObjectValue(), query, attributes, sortAttributes));
231
Map<String, Object> groupByValues = getAttributeValues(groupByAttributes, extractors, element);
232
Set<?> groupId = new HashSet(groupByValues.values());
233
BaseResult group = groupByResults.get(groupId);
235
group = new GroupedResultImpl(query, attributes, sortAttributes, Collections.EMPTY_LIST /* placeholder for now */,
237
groupByResults.put(groupId, group);
239
List<AggregatorInstance<?>> groupAggrs = groupByAggregators.get(groupId);
240
if (groupAggrs == null) {
241
groupAggrs = new ArrayList<AggregatorInstance<?>>(aggregators.size());
242
for (AggregatorInstance<?> aggr : aggregators) {
243
groupAggrs.add(aggr.createClone());
245
groupByAggregators.put(groupId, groupAggrs);
247
// Switch to per-record aggregators
248
aggregators = groupAggrs;
252
aggregate(aggregators, extractors, element);
256
if (hasOrder || isGroupBy) {
258
results = new ArrayList<BaseResult>(results);
262
Collections.sort((List<BaseResult>)results, new OrderComparator(query.getOrdering()));
264
// trim results to max length if necessary
265
int max = query.maxResults();
266
if (max >= 0 && (results.size() > max)) {
267
results = ((List<BaseResult>)results).subList(0, max);
271
if (!aggregators.isEmpty()) {
272
for (BaseResult result : results) {
274
GroupedResultImpl group = (GroupedResultImpl)result;
275
Set<?> groupId = new HashSet(group.getGroupByValues().values());
276
aggregators = groupByAggregators.get(groupId);
278
setResultAggregators(aggregators, result);
282
if (!isGroupBy && anyMatches && !includeResults && !aggregators.isEmpty()) {
283
// add one row in the results if the only thing included was aggregators and anything matched
284
BaseResult aggOnly = new AggregateOnlyResult(query);
285
setResultAggregators(aggregators, aggOnly);
286
results.add(aggOnly);
289
return new ResultsImpl((List)results, query.requestsKeys(), query.requestsValues(), !query.requestedAttributes().isEmpty(), anyMatches
290
&& !aggregators.isEmpty());
293
private void setResultAggregators(List<AggregatorInstance<?>> aggregators, BaseResult result)
295
List<Object> aggregateResults = new ArrayList<Object>();
296
for (AggregatorInstance<?> aggregator : aggregators) {
297
aggregateResults.add(aggregator.aggregateResult());
300
if (!aggregateResults.isEmpty()) {
301
result.setAggregateResults(aggregateResults);
305
private Map<String, Object> getAttributeValues(Set<Attribute<?>> attributes, Map<String, AttributeExtractor> extractors, Element element) {
306
final Map<String, Object> values;
307
if (attributes.isEmpty()) {
308
values = Collections.emptyMap();
310
values = new HashMap<String, Object>();
311
for (Attribute attribute : attributes) {
312
String name = attribute.getAttributeName();
313
values.put(name, getExtractor(name, extractors).attributeFor(element, name));
319
private void aggregate(List<AggregatorInstance<?>> aggregators, Map<String, AttributeExtractor> extractors, Element element) {
320
for (AggregatorInstance<?> aggregator : aggregators) {
321
Attribute<?> attribute = aggregator.getAttribute();
322
if (attribute == null) {
323
aggregator.accept(null);
325
Object val = getExtractor(attribute.getAttributeName(), extractors).attributeFor(element, attribute.getAttributeName());
326
aggregator.accept(val);
331
private Object[] getSortAttributes(StoreQuery query, Map<String, AttributeExtractor> extractors, Element element) {
332
Object[] sortAttributes;
333
List<StoreQuery.Ordering> orderings = query.getOrdering();
334
if (orderings.isEmpty()) {
335
sortAttributes = EMPTY_OBJECT_ARRAY;
337
sortAttributes = new Object[orderings.size()];
338
for (int i = 0; i < sortAttributes.length; i++) {
339
String name = orderings.get(i).getAttribute().getAttributeName();
340
sortAttributes[i] = getExtractor(name, extractors).attributeFor(element, name);
344
return sortAttributes;
348
public void remove(String cacheName, Object uniqueKey, int segmentId, boolean isRemoval) {
349
throw new UnsupportedOperationException();
353
public void clear(String cacheName, int segmentId) {
354
throw new UnsupportedOperationException();