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.cache.invalidation;
023:
024: import javax.transaction.Transaction;
025:
026: import org.jboss.logging.Logger;
027: import org.jboss.tm.TransactionLocal;
028:
029: import java.util.HashMap;
030: import javax.transaction.Synchronization;
031:
032: import java.io.Serializable;
033: import java.util.HashSet;
034: import java.util.Iterator;
035: import java.util.Map;
036: import java.util.Set;
037:
038: /**
039: * Utility class that can be used to group invalidations in a set of
040: * BatchInvalidations structure and only commit them alltogether at
041: * transaction commit-time.
042: * The invalidations are grouped (in this order):
043: * - by transaction
044: * - by InvalidationManager instance
045: * - by InvalidationGroup
046: * <p/>
047: * This object will manage the transaction registering by itself if not
048: * already done.
049: * Thus, once a transaction commits, it will prepare a set of BatchInvalidation
050: * collections (one for each InvalidationManager involved): on BI instance
051: * for each InvalidationGroup. Then it will call the IM.batchInvalidation
052: * method.
053: *
054: * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
055: * @version $Revision: 57209 $
056: * @see InvalidationManagerMBean
057: * @see BatchInvalidation
058: * @see InvalidatorSynchronization
059: */
060: public class InvalidationsTxGrouper {
061:
062: // Constants -----------------------------------------------------
063:
064: // Attributes ----------------------------------------------------
065:
066: // Static --------------------------------------------------------
067:
068: private static final TransactionLocal synchLocal = new TransactionLocal();
069: static Logger log = Logger.getLogger(InvalidationsTxGrouper.class);
070:
071: // Constructors --------------------------------------------------
072:
073: // Public --------------------------------------------------------
074:
075: public static void registerInvalidationSynchronization(
076: Transaction tx, InvalidationGroup group, Serializable key)
077: throws Exception {
078: InvalidatorSynchronization synch = (InvalidatorSynchronization) synchLocal
079: .get(tx);
080: if (synch == null) {
081: synch = new InvalidatorSynchronization(tx);
082: synchLocal.set(tx, synch);
083: // If there is no tx don't try to use it
084: if (tx != null)
085: tx.registerSynchronization(synch);
086: }
087: synch.addInvalidation(group, key);
088: // If there is no tx call afterCompletion
089: if (tx == null)
090: synch
091: .afterCompletion(javax.transaction.Status.STATUS_NO_TRANSACTION);
092: }
093: }
094:
095: class InvalidatorSynchronization implements Synchronization {
096: /**
097: * The transaction we follow.
098: */
099: protected Transaction tx;
100:
101: /**
102: * The context we manage.
103: */
104: protected HashMap ids = new HashMap();
105:
106: /**
107: * Create a new isynchronization instance.
108: */
109: InvalidatorSynchronization(Transaction tx) {
110: this .tx = tx;
111: }
112:
113: public void addInvalidation(InvalidationGroup group,
114: Serializable key) {
115: InvalidationManagerMBean im = group.getInvalidationManager();
116:
117: // the grouping is (in order): by InvalidationManager, by InvalidationGroup
118: //
119:
120: Map relatedInvalidationMgr;
121: synchronized (ids) {
122: relatedInvalidationMgr = (HashMap) ids.get(im);
123: if (relatedInvalidationMgr == null) {
124: relatedInvalidationMgr = new HashMap();
125: ids.put(im, relatedInvalidationMgr);
126: }
127: }
128:
129: Set relatedInvalidations;
130: synchronized (relatedInvalidationMgr) {
131: relatedInvalidations = (HashSet) relatedInvalidationMgr
132: .get(group);
133: if (relatedInvalidations == null) {
134: relatedInvalidations = new HashSet();
135: relatedInvalidationMgr.put(group, relatedInvalidations);
136: }
137: }
138:
139: relatedInvalidations.add(key);
140: }
141:
142: // Synchronization implementation -----------------------------
143:
144: public void beforeCompletion() {
145: }
146:
147: public void afterCompletion(int status) {
148: // This is an independent point of entry. We need to make sure the
149: // thread is associated with the right context class loader
150: //
151: ClassLoader oldCl = Thread.currentThread()
152: .getContextClassLoader();
153: Thread.currentThread().setContextClassLoader(
154: this .getClass().getClassLoader());
155:
156: try {
157: try {
158: sendBatchInvalidations();
159: } catch (Exception ex) {
160: InvalidationsTxGrouper.log.warn(
161: "Failed sending invalidations messages", ex);
162: }
163: } finally {
164: Thread.currentThread().setContextClassLoader(oldCl);
165: }
166: }
167:
168: protected void sendBatchInvalidations() {
169: boolean trace = InvalidationsTxGrouper.log.isTraceEnabled();
170: if (trace) {
171: InvalidationsTxGrouper.log
172: .trace("Begin sendBatchInvalidations, tx=" + tx);
173: }
174: // we iterate over all InvalidationManager involved
175: //
176: Iterator imIter = ids.keySet().iterator();
177: while (imIter.hasNext()) {
178: InvalidationManagerMBean im = (InvalidationManagerMBean) imIter
179: .next();
180:
181: // get associated groups
182: //
183: HashMap relatedInvalidationMgr = (HashMap) ids.get(im);
184:
185: BatchInvalidation[] bomb = new BatchInvalidation[relatedInvalidationMgr
186: .size()];
187:
188: Iterator groupsIter = relatedInvalidationMgr.keySet()
189: .iterator();
190: int i = 0;
191: while (groupsIter.hasNext()) {
192: InvalidationGroup group = (InvalidationGroup) groupsIter
193: .next();
194: HashSet sourceIds = (HashSet) relatedInvalidationMgr
195: .get(group);
196: String groupName = group.getGroupName();
197: if (trace) {
198: InvalidationsTxGrouper.log
199: .trace("Adding ids to bomb(" + groupName
200: + "): " + sourceIds);
201: }
202: Serializable[] ids = new Serializable[sourceIds.size()];
203: sourceIds.toArray(ids);
204: BatchInvalidation batch = new BatchInvalidation(ids,
205: groupName);
206:
207: bomb[i] = batch;
208:
209: i++;
210: }
211:
212: // do the batch-invalidation for this IM
213: //
214: im.batchInvalidate(bomb);
215: }
216: if (trace) {
217: InvalidationsTxGrouper.log
218: .trace("End sendBatchInvalidations, tx=" + tx);
219: }
220:
221: // Help the GC to remove this big structure
222: //
223: this.ids = null;
224: }
225: }
|