/jboss-as-7.1.1.Final/clustering/ejb3-infinispan/src/main/java/org/jboss/as/clustering/ejb3/cache/backing/infinispan/InfinispanBackingCacheEntryStore.java
Java | 343 lines | 281 code | 35 blank | 27 comment | 26 complexity | 5c27310d7072dfa79bc2053fd2f45977 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
1/*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2011, Red Hat Middleware LLC, and individual contributors
4 * as indicated by the @author tags. See the copyright.txt file in the
5 * distribution for a full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22
23package org.jboss.as.clustering.ejb3.cache.backing.infinispan;
24
25import java.io.IOException;
26import java.io.Serializable;
27import java.util.Collections;
28import java.util.List;
29import java.util.Map;
30import java.util.Random;
31import java.util.Set;
32
33import org.infinispan.Cache;
34import org.infinispan.context.Flag;
35import org.infinispan.distribution.DataLocality;
36import org.infinispan.distribution.DistributionManager;
37import org.infinispan.notifications.Listener;
38import org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated;
39import org.infinispan.notifications.cachelistener.annotation.CacheEntryPassivated;
40import org.infinispan.notifications.cachelistener.event.CacheEntryActivatedEvent;
41import org.infinispan.notifications.cachelistener.event.CacheEntryPassivatedEvent;
42import org.infinispan.remoting.transport.Address;
43import org.jboss.as.clustering.MarshalledValue;
44import org.jboss.as.clustering.MarshalledValueFactory;
45import org.jboss.as.clustering.infinispan.invoker.BatchOperation;
46import org.jboss.as.clustering.infinispan.invoker.CacheInvoker;
47import org.jboss.as.clustering.lock.SharedLocalYieldingClusterLockManager;
48import org.jboss.as.clustering.lock.SharedLocalYieldingClusterLockManager.LockResult;
49import org.jboss.as.clustering.lock.TimeoutException;
50import org.jboss.as.clustering.registry.Registry;
51import org.jboss.as.ejb3.cache.Cacheable;
52import org.jboss.as.ejb3.cache.PassivationManager;
53import org.jboss.as.ejb3.cache.impl.backing.clustering.ClusteredBackingCacheEntryStoreConfig;
54import org.jboss.as.ejb3.cache.spi.BackingCacheEntry;
55import org.jboss.as.ejb3.cache.spi.GroupCompatibilityChecker;
56import org.jboss.as.ejb3.cache.spi.impl.AbstractBackingCacheEntryStore;
57import org.jboss.as.ejb3.component.stateful.StatefulTimeoutInfo;
58import org.jboss.ejb.client.Affinity;
59import org.jboss.ejb.client.ClusterAffinity;
60import org.jboss.ejb.client.NodeAffinity;
61import org.jboss.logging.Logger;
62
63/**
64 * Infinispan-based backing cache entry store.
65 * @author Paul Ferraro
66 */
67@Listener
68public class InfinispanBackingCacheEntryStore<K extends Serializable, V extends Cacheable<K>, E extends BackingCacheEntry<K, V>, C> extends AbstractBackingCacheEntryStore<K, V, E>{
69 private final Logger log = Logger.getLogger(getClass());
70
71 private final SharedLocalYieldingClusterLockManager lockManager;
72 private final LockKeyFactory<K, C> lockKeyFactory;
73 private final MarshalledValueFactory<C> keyFactory;
74 private final MarshalledValueFactory<C> valueFactory;
75 private final C context;
76 private final boolean controlCacheLifecycle;
77 private final Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache;
78 private final CacheInvoker invoker;
79 private final PassivationManager<K, E> passivationManager;
80 private final boolean clustered;
81 private final Random random = new Random(System.currentTimeMillis());
82 private final Registry<String, ?> registry;
83
84 public InfinispanBackingCacheEntryStore(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache, CacheInvoker invoker, PassivationManager<K, E> passivationManager, StatefulTimeoutInfo timeout, ClusteredBackingCacheEntryStoreConfig config, boolean controlCacheLifecycle, MarshalledValueFactory<C> keyFactory, MarshalledValueFactory<C> valueFactory, C context, SharedLocalYieldingClusterLockManager lockManager, LockKeyFactory<K, C> lockKeyFactory, Registry<String, ?> registry) {
85 super(timeout, config);
86 this.cache = cache;
87 this.invoker = invoker;
88 this.passivationManager = passivationManager;
89 this.controlCacheLifecycle = controlCacheLifecycle;
90 this.clustered = cache.getCacheConfiguration().clustering().cacheMode().isClustered();
91 this.keyFactory = keyFactory;
92 this.valueFactory = valueFactory;
93 this.context = context;
94 this.lockManager = this.clustered ? lockManager : null;
95 this.lockKeyFactory = lockKeyFactory;
96 this.registry = registry;
97 }
98
99 @Override
100 public void start() {
101 if (this.controlCacheLifecycle) {
102 this.cache.start();
103 }
104 }
105
106 @Override
107 public void stop() {
108 if (this.controlCacheLifecycle) {
109 this.cache.stop();
110 }
111 }
112
113 @Override
114 public boolean hasAffinity(K key) {
115 DistributionManager dist = this.cache.getAdvancedCache().getDistributionManager();
116 if (dist != null) {
117 DataLocality locality = dist.getLocality(key);
118 return locality.isLocal() || locality.isUncertain();
119 }
120 return true;
121 }
122
123 @Override
124 public Affinity getStrictAffinity() {
125 return new ClusterAffinity(this.cache.getCacheManager().getClusterName());
126 }
127
128 @Override
129 public Affinity getWeakAffinity(K key) {
130 if (!this.hasAffinity(key)) {
131 // Locate nodes on which the cache entry will reside
132 List<Address> addresses = this.cache.getAdvancedCache().getDistributionManager().locate(key);
133 if (!addresses.contains(this.cache.getCacheManager().getAddress())) {
134 // Otherwise choose random node from hash targets
135 Map.Entry<String, ?> entry = this.registry.getRemoteEntry(addresses.get(random.nextInt(addresses.size())));
136 if (entry != null) {
137 return new NodeAffinity(entry.getKey());
138 }
139 }
140 }
141 return new NodeAffinity(this.registry.getLocalEntry().getKey());
142 }
143
144 @Override
145 public Set<K> insert(E entry) {
146 K id = entry.getId();
147 this.trace("insert(%s)", id);
148 final MarshalledValue<K, C> key = this.marshalKey(id);
149
150 this.acquireSessionOwnership(key, true);
151 try {
152 final MarshalledValue<E, C> value = this.marshalEntry(entry);
153 Operation<Void> operation = new Operation<Void>() {
154 @Override
155 public Void invoke(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache) {
156 cache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP).put(key, value);
157 return null;
158 }
159 };
160 this.invoke(operation);
161 } finally {
162 this.releaseSessionOwnership(key, false);
163 }
164 return Collections.emptySet();
165 }
166
167 @Override
168 public E get(K id, boolean lock) {
169 this.trace("get(%s. %s)", id, lock);
170 final MarshalledValue<K, C> key = this.marshalKey(id);
171
172 if (lock) {
173 this.acquireSessionOwnership(key, false);
174 }
175
176 Operation<MarshalledValue<E, C>> operation = new Operation<MarshalledValue<E, C>>() {
177 @Override
178 public MarshalledValue<E, C> invoke(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache) {
179 return cache.get(key);
180 }
181 };
182 return this.unmarshalEntry(id, this.invoke(operation));
183 }
184
185 @Override
186 public void update(E entry, boolean modified) {
187 K id = entry.getId();
188 this.trace("update(%s, %s)", id, modified);
189 final MarshalledValue<K, C> key = this.marshalKey(id);
190 try {
191 if (modified) {
192 final MarshalledValue<E, C> value = this.marshalEntry(entry);
193 Operation<Void> operation = new Operation<Void>() {
194 @Override
195 public Void invoke(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache) {
196 cache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP).put(key, value);
197 return null;
198 }
199 };
200 this.invoke(operation);
201 }
202 } finally {
203 this.releaseSessionOwnership(key, false);
204 }
205 }
206
207 @Override
208 public E remove(K id) {
209 this.trace("remove(%s)", id);
210 final MarshalledValue<K, C> key = this.marshalKey(id);
211 Operation<MarshalledValue<E, C>> operation = new Operation<MarshalledValue<E, C>>() {
212 @Override
213 public MarshalledValue<E, C> invoke(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache) {
214 return cache.remove(key);
215 }
216 };
217 try {
218 return this.unmarshalEntry(id, this.invoke(operation));
219 } finally {
220 this.releaseSessionOwnership(key, true);
221 }
222 }
223
224 private <R> R invoke(Operation<R> operation) {
225 return this.invoker.invoke(this.cache, new BatchOperation<MarshalledValue<K, C>, MarshalledValue<E, C>, R>(operation));
226 }
227
228 private MarshalledValue<K, C> marshalKey(K key) {
229 try {
230 return this.keyFactory.createMarshalledValue(key);
231 } catch (IOException e) {
232 throw InfinispanEjbMessages.MESSAGES.serializationFailure(e, key);
233 }
234 }
235
236 private K unmarshalKey(MarshalledValue<K, C> key) {
237 try {
238 return key.get(this.context);
239 } catch (Exception e) {
240 throw InfinispanEjbMessages.MESSAGES.deserializationFailure(e, key);
241 }
242 }
243
244 private MarshalledValue<E, C> marshalEntry(E value) {
245 try {
246 return this.valueFactory.createMarshalledValue(value);
247 } catch (IOException e) {
248 throw InfinispanEjbMessages.MESSAGES.serializationFailure(e, value.getId());
249 }
250 }
251
252 private E unmarshalEntry(K id, MarshalledValue<E, C> value) {
253 if (value == null) return null;
254 try {
255 return value.get(this.context);
256 } catch (Exception e) {
257 throw InfinispanEjbMessages.MESSAGES.deserializationFailure(e, id);
258 }
259 }
260
261 private LockResult acquireSessionOwnership(MarshalledValue<K, C> key, boolean newLock) {
262 if (this.lockManager == null) return null;
263
264 Serializable lockKey = this.lockKeyFactory.createLockKey(key);
265
266 this.trace("Acquiring %slock on %s", newLock ? "new " : "", lockKey);
267
268 long timeout = this.cache.getCacheConfiguration().locking().lockAcquisitionTimeout();
269 try {
270 LockResult result = this.lockManager.lock(lockKey, timeout, newLock);
271 this.trace("Lock acquired (%s) on %s", result, lockKey);
272 return result;
273 } catch (TimeoutException e) {
274 throw InfinispanEjbMessages.MESSAGES.lockAcquisitionTimeout(lockKey, timeout);
275 } catch (InterruptedException e) {
276 Thread.currentThread().interrupt();
277 throw InfinispanEjbMessages.MESSAGES.lockAcquisitionInterruption(e, lockKey);
278 }
279 }
280
281 private void releaseSessionOwnership(MarshalledValue<K, C> key, boolean remove) {
282 if (this.lockManager != null) {
283 Serializable lockKey = this.lockKeyFactory.createLockKey(key);
284 this.trace("Releasing %slock on %s", remove ? "and removing " : "", lockKey);
285 this.lockManager.unlock(lockKey, remove);
286 this.trace("Released %slock on %s", remove ? "and removed " : "", lockKey);
287 }
288 }
289
290 @Override
291 public void passivate(E entry) {
292 final MarshalledValue<K, C> key = this.marshalKey(entry.getId());
293 Operation<Void> operation = new Operation<Void>() {
294 @Override
295 public Void invoke(Cache<MarshalledValue<K, C>, MarshalledValue<E, C>> cache) {
296 cache.evict(key);
297 return null;
298 }
299 };
300 this.invoker.invoke(this.cache, operation);
301 }
302
303 @Override
304 public boolean isClustered() {
305 return this.clustered;
306 }
307
308 @Override
309 public boolean isCompatibleWith(GroupCompatibilityChecker other) {
310 if (other instanceof InfinispanBackingCacheEntryStore) {
311 InfinispanBackingCacheEntryStore<?, ?, ?, ?> store = (InfinispanBackingCacheEntryStore<?, ?, ?, ?>) other;
312 return this.cache.getCacheManager() == store.cache.getCacheManager();
313 }
314 return false;
315 }
316
317 @CacheEntryActivated
318 public void activated(CacheEntryActivatedEvent<MarshalledValue<K, C>, MarshalledValue<E, C>> event) {
319 if ((this.passivationManager != null) && !event.isPre()){
320 K key = this.unmarshalKey(event.getKey());
321 this.trace("activated(%s)", key);
322 this.passivationManager.postActivate(this.unmarshalEntry(key, event.getValue()));
323 }
324 }
325
326 @CacheEntryPassivated
327 public void passivated(CacheEntryPassivatedEvent<MarshalledValue<K, C>, MarshalledValue<E, C>> event) {
328 if ((this.passivationManager != null) && event.isPre()) {
329 K key = this.unmarshalKey(event.getKey());
330 this.trace("passivated(%s)", key);
331 this.passivationManager.prePassivate(this.unmarshalEntry(key, event.getValue()));
332 }
333 }
334
335 abstract class Operation<R> implements CacheInvoker.Operation<MarshalledValue<K, C>, MarshalledValue<E, C>, R> {
336 }
337
338 private void trace(String message, Object... args) {
339 if (this.log.isTraceEnabled()) {
340 this.log.tracef(message, args);
341 }
342 }
343}