001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb3.entity;
023:
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import javax.transaction.SystemException;
030: import javax.transaction.Transaction;
031: import javax.transaction.TransactionManager;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.hibernate.cache.Cache;
036: import org.hibernate.cache.CacheException;
037: import org.hibernate.cache.StandardQueryCache;
038: import org.jboss.cache.Fqn;
039: import org.jboss.cache.config.Option;
040: import org.jboss.cache.lock.TimeoutException;
041:
042: /**
043: * Subclass of the standard <code>org.hibernate.cache.TreeCache</code> used as
044: * a workaround until issues related to JBCLUSTER-150 are resolved in Hibernate.
045: *
046: * @author Gavin King
047: * @author Brian Stansberry
048: */
049: public class JBCCache implements Cache {
050:
051: private static final Log log = LogFactory.getLog(JBCCache.class);
052:
053: private static final String ITEM = "item";
054:
055: private org.jboss.cache.TreeCache cache;
056: private final String regionName;
057: private final Fqn regionFqn;
058: private final TransactionManager transactionManager;
059: private boolean localWritesOnly;
060:
061: public JBCCache(org.jboss.cache.TreeCache cache, String regionName,
062: String regionPrefix, TransactionManager transactionManager)
063: throws CacheException {
064: this .cache = cache;
065: this .regionName = regionName;
066: this .regionFqn = Fqn.fromString(SecondLevelCacheUtil
067: .createRegionFqn(regionName, regionPrefix));
068: this .transactionManager = transactionManager;
069: if (cache.getUseRegionBasedMarshalling()) {
070: localWritesOnly = StandardQueryCache.class.getName()
071: .equals(regionName);
072:
073: boolean fetchState = cache.getFetchInMemoryState();
074: try {
075: // We don't want a state transfer for the StandardQueryCache,
076: // as it can include classes from multiple scoped classloaders
077: if (localWritesOnly)
078: cache.setFetchInMemoryState(false);
079:
080: // We always activate
081: activateCacheRegion(regionFqn.toString());
082: } finally {
083: // Restore the normal state transfer setting
084: if (localWritesOnly)
085: cache.setFetchInMemoryState(fetchState);
086: }
087: } else {
088: log
089: .debug("TreeCache is not configured for region based marshalling");
090: }
091: }
092:
093: public Object get(Object key) throws CacheException {
094: Transaction tx = suspend();
095: try {
096: return read(key);
097: } finally {
098: resume(tx);
099: }
100: }
101:
102: public Object read(Object key) throws CacheException {
103: try {
104: return cache.get(new Fqn(regionFqn, key), ITEM);
105: } catch (Exception e) {
106: throw new CacheException(e);
107: }
108: }
109:
110: public void update(Object key, Object value) throws CacheException {
111: try {
112: if (localWritesOnly) {
113: Option option = new Option();
114: option.setCacheModeLocal(true);
115: cache.put(new Fqn(regionFqn, key), ITEM, value, option);
116: } else {
117: cache.put(new Fqn(regionFqn, key), ITEM, value);
118: }
119: } catch (Exception e) {
120: throw new CacheException(e);
121: }
122: }
123:
124: public void put(Object key, Object value) throws CacheException {
125: Transaction tx = suspend();
126: try {
127: if (localWritesOnly) {
128: Option option = new Option();
129: option.setCacheModeLocal(true);
130: // Overloaded method isn't available, so have to use InvocationContext
131: cache.getInvocationContext().setOptionOverrides(option);
132: try {
133: // do the failfast put outside the scope of the JTA txn
134: cache.putFailFast(new Fqn(regionFqn, key), ITEM,
135: value, 0);
136: } finally {
137: cache.getInvocationContext().setOptionOverrides(
138: null);
139: }
140: } else {
141: //do the failfast put outside the scope of the JTA txn
142: cache.putFailFast(new Fqn(regionFqn, key), ITEM, value,
143: 0);
144: }
145: } catch (TimeoutException te) {
146: //ignore!
147: log.debug("ignoring write lock acquisition failure");
148: } catch (Exception e) {
149: throw new CacheException(e);
150: } finally {
151: resume(tx);
152: }
153: }
154:
155: private void resume(Transaction tx) {
156: try {
157: if (tx != null)
158: transactionManager.resume(tx);
159: } catch (Exception e) {
160: throw new CacheException("Could not resume transaction", e);
161: }
162: }
163:
164: private Transaction suspend() {
165: Transaction tx = null;
166: try {
167: if (transactionManager != null) {
168: tx = transactionManager.suspend();
169: }
170: } catch (SystemException se) {
171: throw new CacheException("Could not suspend transaction",
172: se);
173: }
174: return tx;
175: }
176:
177: public void remove(Object key) throws CacheException {
178: try {
179: if (localWritesOnly) {
180: Option option = new Option();
181: option.setCacheModeLocal(true);
182: cache.remove(new Fqn(regionFqn, key), option);
183: } else {
184: cache.remove(new Fqn(regionFqn, key));
185: }
186: } catch (Exception e) {
187: throw new CacheException(e);
188: }
189: }
190:
191: public void clear() throws CacheException {
192: try {
193: cache.remove(regionFqn);
194: } catch (Exception e) {
195: throw new CacheException(e);
196: }
197: }
198:
199: public void destroy() throws CacheException {
200: try {
201: // NOTE : Hibernate's class uses evict() but that isn't recursive!
202: //cache.evict( regionFqn );
203: Option opt = new Option();
204: opt.setCacheModeLocal(true);
205: cache.remove(regionFqn, opt);
206:
207: if (cache.getUseRegionBasedMarshalling()
208: && !SecondLevelCacheUtil
209: .isSharedClassLoaderRegion(regionName)) {
210: inactivateCacheRegion();
211: }
212: } catch (CacheException e) {
213: throw e;
214: } catch (Exception e) {
215: throw new CacheException(e);
216: }
217: }
218:
219: public void lock(Object key) throws CacheException {
220: throw new UnsupportedOperationException(
221: "TreeCache is a fully transactional cache: "
222: + regionName);
223: }
224:
225: public void unlock(Object key) throws CacheException {
226: throw new UnsupportedOperationException(
227: "TreeCache is a fully transactional cache: "
228: + regionName);
229: }
230:
231: public long nextTimestamp() {
232: return System.currentTimeMillis() / 100;
233: }
234:
235: public int getTimeout() {
236: return 600; //60 seconds
237: }
238:
239: public String getRegionName() {
240: return regionName;
241: }
242:
243: public long getSizeInMemory() {
244: return -1;
245: }
246:
247: public long getElementCountInMemory() {
248: try {
249: Set children = cache.getChildrenNames(regionFqn);
250: return children == null ? 0 : children.size();
251: } catch (Exception e) {
252: throw new CacheException(e);
253: }
254: }
255:
256: public long getElementCountOnDisk() {
257: return 0;
258: }
259:
260: public Map toMap() {
261: try {
262: Map result = new HashMap();
263: Set childrenNames = cache.getChildrenNames(regionFqn);
264: if (childrenNames != null) {
265: Iterator iter = childrenNames.iterator();
266: while (iter.hasNext()) {
267: Object key = iter.next();
268: result.put(key, cache.get(new Fqn(regionFqn, key),
269: ITEM));
270: }
271: }
272: return result;
273: } catch (Exception e) {
274: throw new CacheException(e);
275: }
276: }
277:
278: public String toString() {
279: return "JBCCache(" + regionName + ')';
280: }
281:
282: private void activateCacheRegion(String regionName)
283: throws CacheException {
284: String fqnString = regionFqn.toString();
285: // FIXME -- find a way that doesn't involve this API
286: if (cache.getMarshaller().isInactive(fqnString)) {
287: try {
288: // Only register the classloader if it's not a shared region.
289: // If it's shared, no single classloader is valid
290: if (!SecondLevelCacheUtil
291: .isSharedClassLoaderRegion(regionName)) {
292: cache.registerClassLoader(fqnString, Thread
293: .currentThread().getContextClassLoader());
294: }
295: cache.activateRegion(fqnString);
296: } catch (Exception e) {
297: throw new CacheException("Problem activating region "
298: + regionName, e);
299: }
300: } else {
301: log.debug("activateCacheRegion(): Region " + fqnString
302: + " is already active");
303: }
304: }
305:
306: private void inactivateCacheRegion() throws CacheException {
307: String fqnString = regionFqn.toString();
308: // FIXME -- find a way that doesn't involve this API
309: if (!cache.getMarshaller().isInactive(fqnString)) {
310: try {
311: cache.inactivateRegion(fqnString);
312: cache.unregisterClassLoader(fqnString);
313: } catch (Exception e) {
314: throw new CacheException("Problem activating region "
315: + fqnString, e);
316: }
317: } else {
318: log.debug("inactivateCacheRegion(): Region " + fqnString
319: + " is already inactive");
320: }
321: }
322: }
|