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.search;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.HashSet;
27 import java.util.Set;
28
29 import javax.transaction.TransactionManager;
30
31 import net.sf.ehcache.Cache;
32 import net.sf.ehcache.CacheException;
33 import net.sf.ehcache.CacheManager;
34 import net.sf.ehcache.Ehcache;
35 import net.sf.ehcache.Element;
36 import net.sf.ehcache.config.CacheConfiguration;
37 import net.sf.ehcache.config.SearchAttribute;
38 import net.sf.ehcache.config.Searchable;
39 import net.sf.ehcache.config.CacheConfiguration.TransactionalMode;
40 import net.sf.ehcache.search.Person.Gender;
41
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.junit.runners.Parameterized;
47 import org.junit.runners.Parameterized.Parameters;
48
49 import bitronix.tm.TransactionManagerServices;
50
51 @RunWith(Parameterized.class)
52 public class TransactionalSearchTest {
53
54 @Parameters
55 public static Collection<Object[]> data() {
56 return Arrays.asList(new Object[][]{
57 {TransactionalMode.LOCAL}, {TransactionalMode.XA}, {TransactionalMode.XA_STRICT}
58 });
59 }
60
61 @Before
62 public void setupTransactionManager() {
63 switch (txnMode) {
64 case XA:
65 case XA_STRICT:
66 TransactionManagerServices.getConfiguration().setJournal("null").setGracefulShutdownInterval(0).setBackgroundRecoveryIntervalSeconds(1);
67 txnManager = TransactionManagerServices.getTransactionManager();
68 break;
69 default:
70 txnManager = null;
71 break;
72 }
73 }
74
75 @After
76 public void shutdownTransactionManager() throws Exception {
77 switch (txnMode) {
78 case XA:
79 case XA_STRICT:
80 if (txnManager.getTransaction() != null) {
81 txnManager.rollback();
82 }
83 TransactionManagerServices.getTransactionManager().shutdown();
84 txnManager = null;
85 break;
86 default:
87 txnManager = null;
88 break;
89 }
90 }
91
92 private void beginTransaction(CacheManager manager) throws Exception {
93 switch (txnMode) {
94 case XA:
95 case XA_STRICT:
96 txnManager.begin();
97 break;
98 case LOCAL:
99 manager.getTransactionController().begin();
100 break;
101 }
102 }
103
104 private void commitTransaction(CacheManager manager) throws Exception {
105 switch (txnMode) {
106 case XA:
107 case XA_STRICT:
108 txnManager.commit();
109 break;
110 case LOCAL:
111 manager.getTransactionController().commit();
112 break;
113 }
114 }
115
116 private final TransactionalMode txnMode;
117
118 private TransactionManager txnManager;
119
120 public TransactionalSearchTest(TransactionalMode txnMode) {
121 this.txnMode = txnMode;
122 }
123
124 private CacheConfiguration getBaseCacheConfiguration() {
125 return new CacheConfiguration("jta-search", 0).transactionalMode(txnMode);
126 }
127
128 @Test
129 public void testExpressionAttributeExtractorCache() throws Exception {
130 CacheConfiguration config = getBaseCacheConfiguration();
131 config.searchable(new Searchable().
132 searchAttribute(new SearchAttribute().name("age").expression("value.getAge()")).
133 searchAttribute(new SearchAttribute().name("gender").expression("value.getGender()")).
134 searchAttribute(new SearchAttribute().name("name").expression("value.getName()")));
135 testCacheWithConfiguration(config);
136 }
137
138 @Test
139 public void testCustomAttributeExtractorCache() throws Exception {
140 CacheConfiguration config = getBaseCacheConfiguration();
141 config.searchable(new Searchable().
142 searchAttribute(new SearchAttribute().name("age").className("net.sf.ehcache.search.TestAttributeExtractor")).
143 searchAttribute(new SearchAttribute().name("gender").expression("value.getGender()")).
144 searchAttribute(new SearchAttribute().name("name").expression("value.getName()")));
145 testCacheWithConfiguration(config);
146 }
147
148 @Test
149 public void testBeanAttributeExtractorCache() throws Exception {
150 CacheConfiguration config = getBaseCacheConfiguration();
151 config.searchable(new Searchable().
152 searchAttribute(new SearchAttribute().name("age")).
153 searchAttribute(new SearchAttribute().name("gender")).
154 searchAttribute(new SearchAttribute().name("name")));
155 testCacheWithConfiguration(config);
156 }
157
158 private void testCacheWithConfiguration(CacheConfiguration config) throws Exception {
159 CacheManager cacheManager = new CacheManager();
160 Ehcache cache = new Cache(config);
161 cacheManager.addCache(cache);
162
163 assertTrue(cache.isSearchable());
164
165 beginTransaction(cacheManager);
166 try {
167
168 SearchTestUtil.populateData(cache);
169 } finally {
170 commitTransaction(cacheManager);
171 }
172
173 beginTransaction(cacheManager);
174 try {
175
176 cache.put(new Element(-1, new Person("Chris Dennis", 29, Gender.MALE)));
177 cache.put(new Element(-2, new Person("Cassie (Dog)", 1, Gender.FEMALE)));
178 basicQueries(cache);
179 } finally {
180 commitTransaction(cacheManager);
181 }
182 }
183
184 private void basicQueries(Ehcache cache) {
185 Query query;
186 Attribute<Integer> age = cache.getSearchAttribute("age");
187
188 query = cache.createQuery();
189 query.includeKeys();
190 query.addCriteria(age.ne(35));
191 query.end();
192 verify(cache, query, 2, 4);
193
194 query = cache.createQuery();
195 query.includeKeys();
196 query.addCriteria(cache.getSearchAttribute("age").lt(30));
197 query.end();
198 query.execute();
199 verify(cache, query, 2);
200
201 query = cache.createQuery();
202 query.includeKeys();
203 query.addCriteria(cache.getSearchAttribute("age").le(30));
204 query.end();
205 query.execute();
206 verify(cache, query, 2, 4);
207
208 query = cache.createQuery();
209 query.includeKeys();
210 query.addCriteria(cache.getSearchAttribute("age").in(new HashSet(Arrays.asList(23, 35))));
211 query.end();
212 query.execute();
213 verify(cache, query, 1, 2, 3);
214
215 query = cache.createQuery();
216 query.includeKeys();
217 query.addCriteria(cache.getSearchAttribute("age").gt(30));
218 query.end();
219 query.execute();
220 verify(cache, query, 1, 3);
221
222 query = cache.createQuery();
223 query.includeKeys();
224 query.addCriteria(cache.getSearchAttribute("age").between(23, 35, true, false));
225 query.end();
226 query.execute();
227 verify(cache, query, 2, 4);
228
229 query = cache.createQuery();
230 query.includeKeys();
231 query.addCriteria(cache.getSearchAttribute("age").ge(30));
232 query.end();
233 query.execute();
234 verify(cache, query, 1, 3, 4);
235
236 query = cache.createQuery();
237 query.includeKeys();
238 query.addCriteria(cache.getSearchAttribute("age").eq(35).or(cache.getSearchAttribute("gender").eq(Gender.FEMALE)));
239 query.end();
240 verify(cache, query, 1, 2, 3);
241
242 query = cache.createQuery();
243 query.includeKeys();
244 query.addCriteria(cache.getSearchAttribute("age").eq(35).and(cache.getSearchAttribute("gender").eq(Gender.MALE)));
245 query.end();
246 verify(cache, query, 1, 3);
247
248 query = cache.createQuery();
249 query.includeKeys();
250 query.addCriteria(cache.getSearchAttribute("age").eq(35).and(cache.getSearchAttribute("gender").eq(Gender.FEMALE)));
251 query.end();
252 verify(cache, query);
253
254 query = cache.createQuery();
255 query.includeKeys();
256 query.addCriteria(cache.getSearchAttribute("gender").eq(Gender.MALE).not());
257 query.end();
258 verify(cache, query, 2);
259
260 try {
261 cache.getSearchAttribute("DOES_NOT_EXIST_PLEASE_DO_NOT_CREATE_ME");
262 fail();
263 } catch (CacheException ce) {
264
265 }
266 }
267
268 private void verify(Ehcache cache, Query query, Integer... expectedKeys) {
269 Results results = query.execute();
270 assertEquals(expectedKeys.length, results.size());
271 assertTrue(results.hasKeys());
272 assertFalse(results.hasAttributes());
273
274 Set<Integer> keys = new HashSet<Integer>(Arrays.asList(expectedKeys));
275
276 for (Result result : results.all()) {
277 int key = (Integer) result.getKey();
278 if (!keys.remove(key)) {
279 throw new AssertionError("unexpected key: " + key);
280 }
281 }
282 }
283 }