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.util.HashSet;
025: import java.util.Map;
026: import java.util.Iterator;
027: import java.rmi.RemoteException;
028: import javax.transaction.Status;
029: import javax.transaction.SystemException;
030:
031: import org.jboss.ejb.Container;
032: import org.jboss.ejb.StatefulSessionContainer;
033: import org.jboss.ejb.EnterpriseContext;
034: import org.jboss.ejb.StatefulSessionEnterpriseContext;
035: import org.jboss.ejb.StatefulSessionPersistenceManager;
036: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
037:
038: /**
039: * Cache for stateful session beans.
040: *
041: * @author <a href="mailto:simone.bordet@compaq.com">Simone Bordet</a>
042: * @author <a href="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
043: * @version $Revision: 57209 $
044: */
045: public class StatefulSessionInstanceCache extends AbstractInstanceCache {
046: // Constants -----------------------------------------------------
047:
048: // Attributes ----------------------------------------------------
049:
050: /* The container */
051: private StatefulSessionContainer m_container;
052:
053: /** The map<id, Long> that holds passivated bean ids that have been removed
054: * from the cache and passivated to the pm along with the time of passivation
055: */
056: private ConcurrentReaderHashMap passivatedIDs = new ConcurrentReaderHashMap();
057:
058: /* Ids that are currently being activated */
059: private HashSet activating = new HashSet();
060:
061: /* Used for logging */
062: private StringBuffer buffer = new StringBuffer();
063:
064: // Static --------------------------------------------------------
065:
066: // Constructors --------------------------------------------------
067:
068: // Public --------------------------------------------------------
069:
070: /** Get the passivated count.
071: * @jmx:managed-attribute
072: * @return the number of passivated instances.
073: */
074: public long getPassivatedCount() {
075: return passivatedIDs.size();
076: }
077:
078: /* From ContainerPlugin interface */
079: public void setContainer(Container c) {
080: m_container = (StatefulSessionContainer) c;
081: }
082:
083: public void destroy() {
084: synchronized (this ) {
085: this .m_container = null;
086: }
087: passivatedIDs.clear();
088: super .destroy();
089: }
090:
091: // Z implementation ----------------------------------------------
092:
093: // Y overrides ---------------------------------------------------
094:
095: protected synchronized Container getContainer() {
096: return m_container;
097: }
098:
099: protected void passivate(EnterpriseContext ctx)
100: throws RemoteException {
101: m_container.getPersistenceManager().passivateSession(
102: (StatefulSessionEnterpriseContext) ctx);
103: passivatedIDs.put(ctx.getId(), new Long(System
104: .currentTimeMillis()));
105: }
106:
107: protected void activate(EnterpriseContext ctx)
108: throws RemoteException {
109: m_container.getPersistenceManager().activateSession(
110: (StatefulSessionEnterpriseContext) ctx);
111: passivatedIDs.remove(ctx.getId());
112: }
113:
114: protected boolean doActivate(EnterpriseContext ctx)
115: throws RemoteException {
116: Object id = ctx.getId();
117: synchronized (activating) {
118: // This is a recursive invocation
119: if (activating.contains(id))
120: return false;
121: activating.add(id);
122: }
123: try {
124: return super .doActivate(ctx);
125: } finally {
126: synchronized (activating) {
127: activating.remove(id);
128: }
129: }
130: }
131:
132: protected EnterpriseContext acquireContext() throws Exception {
133: return m_container.getInstancePool().get();
134: }
135:
136: protected void freeContext(EnterpriseContext ctx) {
137: m_container.getInstancePool().free(ctx);
138: }
139:
140: protected Object getKey(EnterpriseContext ctx) {
141: return ctx.getId();
142: }
143:
144: protected void setKey(Object id, EnterpriseContext ctx) {
145: ctx.setId(id);
146: }
147:
148: protected boolean canPassivate(EnterpriseContext ctx) {
149: if (ctx.isLocked()) {
150: // The context is in the interceptor chain
151: return false;
152: } else if (m_container.getLockManager().canPassivate(
153: ctx.getId()) == false) {
154: return false;
155: } else {
156: if (ctx.getTransaction() != null) {
157: try {
158: return (ctx.getTransaction().getStatus() == Status.STATUS_NO_TRANSACTION);
159: } catch (SystemException e) {
160: // SA FIXME: not sure what to do here
161: return false;
162: }
163: }
164: }
165: return true;
166: }
167:
168: /** Remove all passivated instances that have been inactive too long.
169: * @param maxLifeAfterPassivation the upper bound in milliseconds that an
170: * inactive session will be kept.
171: */
172: protected void removePassivated(long maxLifeAfterPassivation) {
173: StatefulSessionPersistenceManager store = m_container
174: .getPersistenceManager();
175: long now = System.currentTimeMillis();
176: log.debug("removePassivated, now=" + now
177: + ", maxLifeAfterPassivation="
178: + maxLifeAfterPassivation);
179: boolean trace = log.isTraceEnabled();
180: Iterator entries = passivatedIDs.entrySet().iterator();
181: while (entries.hasNext()) {
182: Map.Entry entry = (Map.Entry) entries.next();
183: Object key = entry.getKey();
184: Long value = (Long) entry.getValue();
185: if (value != null) {
186: long passivationTime = value.longValue();
187: if (now - passivationTime > maxLifeAfterPassivation) {
188: preRemovalPreparation(key);
189: store.removePassivated(key);
190: if (trace)
191: log(key, passivationTime);
192: // Must use iterator to avoid ConcurrentModificationException
193: entries.remove();
194: postRemovalCleanup(key);
195: }
196: }
197: }
198: }
199:
200: // Protected -----------------------------------------------------
201:
202: protected void preRemovalPreparation(Object key) {
203: // no-op...extending classes may add prep
204: }
205:
206: protected void postRemovalCleanup(Object key) {
207: // no-op...extending classes may add cleanup
208: }
209:
210: // Private -------------------------------------------------------
211:
212: private void log(Object key, long passivationTime) {
213: if (log.isTraceEnabled()) {
214: buffer.setLength(0);
215: buffer.append("Removing from storage bean '");
216: buffer.append(m_container.getBeanMetaData().getEjbName());
217: buffer.append("' with id = ");
218: buffer.append(key);
219: buffer.append(", passivationTime=");
220: buffer.append(passivationTime);
221: log.trace(buffer.toString());
222: }
223: }
224:
225: // Inner classes -------------------------------------------------
226:
227: }
|