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.ejb.plugins;
023:
024: import java.lang.reflect.Constructor;
025: import java.rmi.NoSuchObjectException;
026: import java.rmi.RemoteException;
027: import java.util.Map;
028:
029: import org.jboss.deployment.DeploymentException;
030: import org.jboss.ejb.BeanLock;
031: import org.jboss.ejb.BeanLockExt;
032: import org.jboss.ejb.Container;
033: import org.jboss.ejb.EnterpriseContext;
034: import org.jboss.ejb.InstanceCache;
035: import org.jboss.logging.Logger;
036: import org.jboss.metadata.MetaData;
037: import org.jboss.metadata.XmlLoadable;
038: import org.jboss.monitor.MetricsConstants;
039: import org.jboss.monitor.Monitorable;
040: import org.jboss.monitor.client.BeanCacheSnapshot;
041: import org.jboss.util.CachePolicy;
042: import org.w3c.dom.Element;
043:
044: /**
045: * Base class for caches of entity and stateful beans. <p>
046: * It manages the cache entries through a {@link CachePolicy} object;
047: * the implementation of the cache policy object must respect the following
048: * requirements:
049: * <ul>
050: * <li> Have a public constructor that takes a single argument of type
051: * AbstractInstanceCache.class or a subclass
052: * </ul>
053: *
054: * @author <a href="mailto:simone.bordet@compaq.com">Simone Bordet</a>
055: * @author <a href="bill@burkecentral.com">Bill Burke</a>
056: * @author <a href="marc.fleury@jboss.org">Marc Fleury</a>
057: * @author Scott.Stark@jboss.org
058: * @version $Revision: 57209 $
059: * @jmx:mbean
060: */
061: public abstract class AbstractInstanceCache implements InstanceCache,
062: XmlLoadable, Monitorable, MetricsConstants,
063: AbstractInstanceCacheMBean {
064: // Constants -----------------------------------------------------
065:
066: // Attributes ----------------------------------------------------
067: protected static Logger log = Logger
068: .getLogger(AbstractInstanceCache.class);
069:
070: /* The object that is delegated to implement the desired caching policy */
071: private CachePolicy m_cache;
072: /* The mutex object for the cache */
073: private final Object m_cacheLock = new Object();
074:
075: // Static --------------------------------------------------------
076:
077: // Constructors --------------------------------------------------
078:
079: // Monitorable implementation ------------------------------------
080: public void sample(Object s) {
081: if (m_cache == null)
082: return;
083:
084: synchronized (getCacheLock()) {
085: BeanCacheSnapshot snapshot = (BeanCacheSnapshot) s;
086: snapshot.m_passivatingBeans = 0;
087: CachePolicy policy = getCache();
088: if (policy instanceof Monitorable) {
089: ((Monitorable) policy).sample(s);
090: }
091: }
092: }
093:
094: public Map retrieveStatistic() {
095: return null;
096: }
097:
098: public void resetStatistic() {
099: }
100:
101: // Public --------------------------------------------------------
102: /* From InstanceCache interface */
103: public EnterpriseContext get(Object id) throws RemoteException,
104: NoSuchObjectException {
105: if (id == null)
106: throw new IllegalArgumentException(
107: "Can't get an object with a null key");
108:
109: EnterpriseContext ctx;
110:
111: synchronized (getCacheLock()) {
112: CachePolicy cache = getCache();
113: ctx = (EnterpriseContext) cache.get(id);
114: if (ctx == null) {
115: try {
116: ctx = acquireContext();
117: setKey(id, ctx);
118: if (doActivate(ctx) == false)
119: // This is a recursive activation
120: return ctx;
121: logActivation(id);
122: // the cache will throw an IllegalStateException if we try to insert
123: // something that is in the cache already, so we don't check here
124: cache.insert(id, ctx);
125: } catch (Throwable x) {
126: log.debug("Activation failure", x);
127: throw new NoSuchObjectException(x.getMessage());
128: }
129: }
130: }
131:
132: return ctx;
133: }
134:
135: /* From InstanceCache interface */
136: public void insert(EnterpriseContext ctx) {
137: if (ctx == null)
138: throw new IllegalArgumentException(
139: "Can't insert a null object in the cache");
140:
141: Object key = getKey(ctx);
142: synchronized (getCacheLock()) {
143: // the cache will throw an IllegalStateException if we try to insert
144: // something that is in the cache already, so we don't check here
145: getCache().insert(key, ctx);
146: }
147: }
148:
149: /**
150: * Tries to passivate the instance. If the instance is in use then the instance
151: * will be passivated later according to the container's commit option and max age.
152: */
153: protected void tryToPassivate(EnterpriseContext ctx) {
154: tryToPassivate(ctx, false);
155: }
156:
157: /**
158: * Tries to passivate the instance. If the instance is in use and passivateAfterCommit
159: * parameter is true then the instance will passivated after the transaction commits.
160: * Otherwise, the instance will be passivated later according to the container's
161: * commit option and max age.
162: */
163: protected void tryToPassivate(EnterpriseContext ctx,
164: boolean passivateAfterCommit) {
165: Object id = ctx.getId();
166: if (id == null)
167: return;
168: BeanLock lock = getContainer().getLockManager().getLock(id);
169: boolean lockedBean = false;
170: try {
171: /* If this is a BeanLockExt only attempt the lock as the call to
172: remove is going to have to acquire the cache lock, but this may already
173: be held since this method is called by passivation policies without
174: the cache lock. This can lead to a deadlock as in the case of a size based
175: eviction during a cache get attempts to lock the bean that has been
176: locked by an age based background thread as seen in bug 987389 on
177: sourceforge.
178: */
179: if (lock instanceof BeanLockExt) {
180: BeanLockExt lock2 = (BeanLockExt) lock;
181: lockedBean = lock2.attemptSync();
182: if (lockedBean == false) {
183: unableToPassivateDueToCtxLock(ctx,
184: passivateAfterCommit);
185: return;
186: }
187: } else {
188: // Use the blocking sync
189: lock.sync();
190: lockedBean = true;
191: }
192:
193: if (canPassivate(ctx)) {
194: try {
195: remove(id);
196: passivate(ctx);
197: freeContext(ctx);
198: } catch (Exception ignored) {
199: log.warn("failed to passivate, id=" + id, ignored);
200: }
201: } else {
202: // Touch the entry to make it MRU
203: synchronized (getCacheLock()) {
204: getCache().get(id);
205: }
206:
207: unableToPassivateDueToCtxLock(ctx, passivateAfterCommit);
208: }
209: } finally {
210: if (lockedBean) {
211: lock.releaseSync();
212: }
213:
214: // getLock is adding a ref count so we need to decrement it.
215: getContainer().getLockManager().removeLockRef(id);
216: }
217: }
218:
219: /**
220: * Passivates and removes the instance from the cache.
221: * If the instance is in use then removal and passivation will be scheduled until
222: * after transaction ends
223: */
224: public void release(EnterpriseContext ctx) {
225: if (ctx == null)
226: throw new IllegalArgumentException(
227: "Can't release a null object");
228:
229: // Here I remove the bean; call to remove(id) is wrong
230: // cause will remove also the cache lock that is needed
231: // by the passivation, that eventually will remove it.
232: /* the removal should only be done if the instance is not in use.
233: this is taken care of in tryToPassivate
234: Object id = getKey(ctx);
235: synchronized (getCacheLock())
236: {
237: if (getCache().peek(id) != null)
238: getCache().remove(id);
239: }
240: */
241: tryToPassivate(ctx, true);
242: }
243:
244: /**
245: * From InstanceCache interface
246: * @jmx:managed-operation
247: */
248: public void remove(Object id) {
249: if (id == null)
250: throw new IllegalArgumentException(
251: "Can't remove an object using a null key");
252:
253: synchronized (getCacheLock()) {
254: if (getCache().peek(id) != null) {
255: getCache().remove(id);
256: }
257: }
258: }
259:
260: public boolean isActive(Object id) {
261: // Check whether an object with the given id is available in the cache
262: synchronized (getCacheLock()) {
263: return getCache().peek(id) != null;
264: }
265: }
266:
267: /** Get the current cache size
268: * @jmx:managed-attribute
269: * @return the size of the cache
270: */
271: public long getCacheSize() {
272: int cacheSize = m_cache != null ? m_cache.size() : 0;
273: return cacheSize;
274: }
275:
276: /** Flush the cache.
277: * @jmx:managed-operation
278: */
279: public void flush() {
280: if (m_cache != null)
281: m_cache.flush();
282: }
283:
284: /** Get the passivated count.
285: * @jmx:managed-attribute
286: * @return the number of passivated instances.
287: */
288: public long getPassivatedCount() {
289: return 0;
290: }
291:
292: /**
293: * Display the cache policy.
294: *
295: * @jmx:managed-attribute
296: * @return the cache policy as a string.
297: */
298: public String getCachePolicyString() {
299: return m_cache.toString();
300: }
301:
302: // XmlLoadable implementation ----------------------------------------------
303: public void importXml(Element element) throws DeploymentException {
304: // This one is mandatory
305: String p = MetaData.getElementContent(MetaData.getUniqueChild(
306: element, "cache-policy"));
307: try {
308: Class cls = SecurityActions.getContextClassLoader()
309: .loadClass(p);
310: Constructor ctor = cls
311: .getConstructor(new Class[] { AbstractInstanceCache.class });
312: m_cache = (CachePolicy) ctor
313: .newInstance(new Object[] { this });
314: } catch (Exception x) {
315: throw new DeploymentException("Can't create cache policy",
316: x);
317: }
318:
319: Element policyConf = MetaData.getOptionalChild(element,
320: "cache-policy-conf");
321: if (policyConf != null) {
322: if (m_cache instanceof XmlLoadable) {
323: try {
324: ((XmlLoadable) m_cache).importXml(policyConf);
325: } catch (Exception x) {
326: throw new DeploymentException(
327: "Can't import policy configuration", x);
328: }
329: }
330: }
331: }
332:
333: /* From Service interface*/
334: public void create() throws Exception {
335: getCache().create();
336: }
337:
338: /* From Service interface*/
339: public void start() throws Exception {
340: getCache().start();
341: }
342:
343: /* From Service interface*/
344: public void stop() {
345: // Empty the cache
346: synchronized (getCacheLock()) {
347: getCache().stop();
348: }
349: }
350:
351: /* From Service interface*/
352: public void destroy() {
353: synchronized (getCacheLock()) {
354: getCache().destroy();
355: }
356: this .m_cache = null;
357: }
358:
359: // Y overrides ---------------------------------------------------
360:
361: // Package protected ---------------------------------------------
362:
363: // Protected -----------------------------------------------------
364: protected void logActivation(Object id) {
365: if (log.isTraceEnabled()) {
366: StringBuffer m_buffer = new StringBuffer(100);
367: m_buffer.append("Activated bean ");
368: m_buffer.append(getContainer().getBeanMetaData()
369: .getEjbName());
370: m_buffer.append(" with id = ");
371: m_buffer.append(id);
372: log.trace(m_buffer.toString());
373: }
374: }
375:
376: protected void logPassivation(Object id) {
377: if (log.isTraceEnabled()) {
378: StringBuffer m_buffer = new StringBuffer(100);
379: m_buffer.append("Passivated bean ");
380: m_buffer.append(getContainer().getBeanMetaData()
381: .getEjbName());
382: m_buffer.append(" with id = ");
383: m_buffer.append(id);
384: log.trace(m_buffer.toString());
385: }
386: }
387:
388: protected void unableToPassivateDueToCtxLock(EnterpriseContext ctx,
389: boolean passivateAfterCommit) {
390: log.warn("Unable to passivate due to ctx lock, id="
391: + ctx.getId());
392: }
393:
394: /**
395: * Returns the container for this cache.
396: */
397: protected abstract Container getContainer();
398:
399: /**
400: * Returns the cache policy used for this cache.
401: */
402: protected CachePolicy getCache() {
403: return m_cache;
404: }
405:
406: /**
407: * Returns the mutex used to sync access to the cache policy object
408: */
409: public Object getCacheLock() {
410: return m_cacheLock;
411: }
412:
413: /**
414: * Passivates the given EnterpriseContext
415: */
416: protected abstract void passivate(EnterpriseContext ctx)
417: throws RemoteException;
418:
419: /**
420: * Activates the given EnterpriseContext
421: */
422: protected abstract void activate(EnterpriseContext ctx)
423: throws RemoteException;
424:
425: /**
426: * Activate the given EnterpriseContext
427: *
428: * @param ctx the context
429: * @return false if we recursively activating
430: * @throws RemoteException for any error
431: */
432: protected boolean doActivate(EnterpriseContext ctx)
433: throws RemoteException {
434: activate(ctx);
435: return true;
436: }
437:
438: /**
439: * Acquires an EnterpriseContext from the pool
440: */
441: protected abstract EnterpriseContext acquireContext()
442: throws Exception;
443:
444: /**
445: * Frees the given EnterpriseContext to the pool
446: */
447: protected abstract void freeContext(EnterpriseContext ctx);
448:
449: /**
450: * Returns the key used by the cache to map the given context
451: */
452: protected abstract Object getKey(EnterpriseContext ctx);
453:
454: /**
455: * Sets the given id as key for the given context
456: */
457: protected abstract void setKey(Object id, EnterpriseContext ctx);
458:
459: /**
460: * Returns whether the given context can be passivated or not
461: *
462: */
463: protected abstract boolean canPassivate(EnterpriseContext ctx);
464:
465: // Private -------------------------------------------------------
466:
467: // Inner classes -------------------------------------------------
468: }
|