1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.store;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.Ehcache;
21 import net.sf.ehcache.Element;
22 import net.sf.ehcache.config.CacheConfiguration;
23 import net.sf.ehcache.pool.Pool;
24 import net.sf.ehcache.search.Attribute;
25 import net.sf.ehcache.search.Result;
26 import net.sf.ehcache.search.Results;
27 import net.sf.ehcache.search.aggregator.AggregatorInstance;
28 import net.sf.ehcache.search.attribute.AttributeExtractor;
29 import net.sf.ehcache.search.expression.Criteria;
30 import net.sf.ehcache.search.impl.AggregateOnlyResult;
31 import net.sf.ehcache.search.impl.BaseResult;
32 import net.sf.ehcache.search.impl.OrderComparator;
33 import net.sf.ehcache.search.impl.ResultImpl;
34 import net.sf.ehcache.search.impl.ResultsImpl;
35 import net.sf.ehcache.transaction.SoftLock;
36
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.concurrent.ConcurrentHashMap;
43
44 /***
45 * A memory-only store with support for all caching features.
46 *
47 * @author Ludovic Orban
48 */
49 public final class MemoryOnlyStore extends FrontEndCacheTier<NullStore, MemoryStore> {
50
51 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
52
53 private final Map<String, AttributeExtractor> attributeExtractors = new ConcurrentHashMap<String, AttributeExtractor>();
54 private final Map<String, Attribute> searchAttributes = new ConcurrentHashMap<String, Attribute>();
55
56
57 private MemoryOnlyStore(CacheConfiguration cacheConfiguration, NullStore cache, MemoryStore authority) {
58 super(cache, authority, cacheConfiguration.getCopyStrategy(), cacheConfiguration.isCopyOnWrite(), cacheConfiguration.isCopyOnRead());
59 }
60
61 /***
62 * Create an instance of MemoryStore
63 * @param cache the cache
64 * @param onHeapPool the on heap pool
65 * @return an instance of MemoryStore
66 */
67 public static Store create(Ehcache cache, Pool onHeapPool) {
68 final NullStore nullStore = NullStore.create();
69 final MemoryStore memoryStore = MemoryStore.create(cache, onHeapPool);
70 return new MemoryOnlyStore(cache.getCacheConfiguration(), nullStore, memoryStore);
71 }
72
73 /***
74 * {@inheritDoc}
75 */
76 @Override
77 public void setAttributeExtractors(Map<String, AttributeExtractor> extractors) {
78 this.attributeExtractors.putAll(extractors);
79
80 for (String name : extractors.keySet()) {
81 searchAttributes.put(name, new Attribute(name));
82 }
83 }
84
85 /***
86 * {@inheritDoc}
87 */
88 @Override
89 public Results executeQuery(StoreQuery query) {
90 Criteria c = query.getCriteria();
91
92 List<AggregatorInstance<?>> aggregators = query.getAggregatorInstances();
93
94
95 boolean includeResults = query.requestsKeys() || query.requestsValues() || !query.requestedAttributes().isEmpty();
96
97 ArrayList<Result> results = new ArrayList<Result>();
98
99 boolean hasOrder = !query.getOrdering().isEmpty();
100
101 boolean anyMatches = false;
102
103 for (Element element : authority.elementSet()) {
104 if (!hasOrder && query.maxResults() >= 0 && results.size() == query.maxResults()) {
105 break;
106 }
107 if (element.getObjectValue() instanceof SoftLock) {
108 continue;
109 }
110
111 if (c.execute(element, attributeExtractors)) {
112 anyMatches = true;
113
114 if (includeResults) {
115 final Map<String, Object> attributes;
116 if (query.requestedAttributes().isEmpty()) {
117 attributes = Collections.emptyMap();
118 } else {
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));
123 }
124 }
125
126 final Object[] sortAttributes;
127 List<StoreQuery.Ordering> orderings = query.getOrdering();
128 if (orderings.isEmpty()) {
129 sortAttributes = EMPTY_OBJECT_ARRAY;
130 } else {
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);
135 }
136 }
137
138
139 results.add(new ResultImpl(element.getObjectKey(), element.getObjectValue(), query, attributes, sortAttributes));
140 }
141
142 for (AggregatorInstance<?> aggregator : aggregators) {
143 Attribute<?> attribute = aggregator.getAttribute();
144 if (attribute == null) {
145 aggregator.accept(null);
146 } else {
147 Object val = attributeExtractors.get(attribute.getAttributeName()).attributeFor(element,
148 attribute.getAttributeName());
149 aggregator.accept(val);
150 }
151 }
152 }
153 }
154
155 if (hasOrder) {
156 Collections.sort(results, new OrderComparator(query.getOrdering()));
157
158
159 int max = query.maxResults();
160 if (max >= 0 && (results.size() > max)) {
161 results.subList(max, results.size()).clear();
162 results.trimToSize();
163 }
164 }
165
166
167 List<Object> aggregateResults = aggregators.isEmpty() ? Collections.emptyList() : new ArrayList<Object>();
168 for (AggregatorInstance<?> aggregator : aggregators) {
169 aggregateResults.add(aggregator.aggregateResult());
170 }
171
172 if (anyMatches && !includeResults && !aggregateResults.isEmpty()) {
173
174 results.add(new AggregateOnlyResult(query));
175 }
176
177
178 if (!aggregateResults.isEmpty()) {
179 for (Result result : results) {
180
181 ((BaseResult)result).setAggregateResults(aggregateResults);
182 }
183 }
184
185 return new ResultsImpl(results, query.requestsKeys(), query.requestsValues(),
186 !query.requestedAttributes().isEmpty(), anyMatches && !aggregateResults.isEmpty());
187 }
188
189 /***
190 * {@inheritDoc}
191 */
192 @Override
193 public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
194 return searchAttributes.get(attributeName);
195 }
196
197 /***
198 * {@inheritDoc}
199 */
200 public Object getMBean() {
201 return null;
202 }
203 }