KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > interceptors > InvalidationInterceptor


1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.cache.interceptors;
8
9 import org.jboss.cache.CacheSPI;
10 import org.jboss.cache.Fqn;
11 import org.jboss.cache.GlobalTransaction;
12 import org.jboss.cache.InvocationContext;
13 import org.jboss.cache.OptimisticTransactionEntry;
14 import org.jboss.cache.TransactionEntry;
15 import org.jboss.cache.TransactionTable;
16 import org.jboss.cache.config.Configuration;
17 import org.jboss.cache.config.Option;
18 import org.jboss.cache.marshall.MethodCall;
19 import org.jboss.cache.marshall.MethodCallFactory;
20 import org.jboss.cache.marshall.MethodDeclarations;
21 import org.jboss.cache.optimistic.TransactionWorkspace;
22
23 import javax.transaction.SystemException JavaDoc;
24 import javax.transaction.Transaction JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.LinkedList JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31
32 /**
33  * This interceptor acts as a replacement to the replication interceptor when
34  * the CacheImpl is configured with ClusteredSyncMode as INVALIDATE.
35  * <p/>
36  * The idea is that rather than replicating changes to all caches in a cluster
37  * when CRUD (Create, Remove, Update, Delete) methods are called, simply call
38  * evict(Fqn) on the remote caches for each changed node. This allows the
39  * remote node to look up the value in a shared cache loader which would have
40  * been updated with the changes.
41  *
42  * @author <a HREF="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
43  */

44 public class InvalidationInterceptor extends BaseRpcInterceptor implements InvalidationInterceptorMBean
45 {
46    private long m_invalidations = 0;
47    protected TransactionTable txTable;
48
49    public void setCache(CacheSPI cache)
50    {
51       super.setCache(cache);
52       txTable = cache.getTransactionTable();
53    }
54
55    public Object JavaDoc invoke(MethodCall m) throws Throwable JavaDoc
56    {
57       InvocationContext ctx = cache.getInvocationContext();
58       Option optionOverride = ctx.getOptionOverrides();
59       if (optionOverride != null && optionOverride.isCacheModeLocal() && ctx.getTransaction() == null)
60       {
61          // skip replication!!
62
return super.invoke(m);
63       }
64
65       Transaction tx = ctx.getTransaction();
66       Object JavaDoc retval = super.invoke(m);
67
68       if (log.isTraceEnabled()) log.trace("(" + cache.getLocalAddress() + ") method call " + m);
69
70       // now see if this is a CRUD method:
71
if (MethodDeclarations.isCrudMethod(m.getMethodId()))
72       {
73          if (log.isDebugEnabled()) log.debug("Is a CRUD method");
74          Fqn fqn = findFqn(m.getArgs());
75          if (fqn != null)
76          {
77             // could be potentially TRANSACTIONAL. Ignore if it is, until we see a prepare().
78
if (tx == null || !isValid(tx))
79             {
80                // the no-tx case:
81
//replicate an evict call.
82
invalidateAcrossCluster(fqn, null);
83             }
84          }
85       }
86       else
87       {
88          // not a CRUD method - lets see if it is a tx lifecycle method.
89
if (tx != null && isValid(tx))
90          {
91             // lets see if we are in the prepare phase (as this is the only time we actually do anything)
92
switch (m.getMethodId())
93             {
94                case MethodDeclarations.prepareMethod_id:
95                case MethodDeclarations.optimisticPrepareMethod_id:
96                   log.debug("Entering InvalidationInterceptor's prepare phase");
97                   // fetch the modifications before the transaction is committed (and thus removed from the txTable)
98
GlobalTransaction gtx = ctx.getGlobalTransaction();
99                   TransactionEntry entry = txTable.get(gtx);
100                   if (entry == null) throw new IllegalStateException JavaDoc("cannot find transaction entry for " + gtx);
101                   List JavaDoc<MethodCall> modifications = new LinkedList JavaDoc<MethodCall>(entry.getModifications());
102
103                   if (modifications.size() > 0)
104                   {
105                      try
106                      {
107                         invalidateModifications(modifications, configuration.isNodeLockingOptimistic() ? getWorkspace(gtx) : null);
108                      }
109                      catch (Throwable JavaDoc t)
110                      {
111                         log.warn("Unable to broadcast evicts as a part of the prepare phase. Rolling back.", t);
112                         try
113                         {
114                            tx.setRollbackOnly();
115                         }
116                         catch (SystemException JavaDoc se)
117                         {
118                            throw new RuntimeException JavaDoc("setting tx rollback failed ", se);
119                         }
120                         throw new RuntimeException JavaDoc("Unable to broadcast invalidation messages", t);
121                      }
122                   }
123                   log.debug("Leaving InvalidationInterceptor's prepare phase");
124                   break;
125             }
126          }
127
128       }
129       return retval;
130    }
131
132    public long getInvalidations()
133    {
134       return m_invalidations;
135    }
136
137    public void resetStatistics()
138    {
139       m_invalidations = 0;
140    }
141
142    public Map JavaDoc<String JavaDoc, Object JavaDoc> dumpStatistics()
143    {
144       Map JavaDoc<String JavaDoc, Object JavaDoc> retval = new HashMap JavaDoc<String JavaDoc, Object JavaDoc>();
145       retval.put("Invalidations", m_invalidations);
146       return retval;
147    }
148
149    protected void invalidateAcrossCluster(Fqn fqn, TransactionWorkspace workspace) throws Throwable JavaDoc
150    {
151       // increment invalidations counter if statistics maintained
152
if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
153          m_invalidations++;
154
155       // only propagate version details if we're using explicit versioning.
156
MethodCall call = workspace != null && !workspace.isVersioningImplicit() ?
157               MethodCallFactory.create(MethodDeclarations.evictVersionedNodeMethodLocal, fqn, workspace.getNode(fqn).getVersion()) :
158               MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal, fqn);
159
160       if (log.isDebugEnabled()) log.debug("Cache [" + cache.getLocalAddress() + "] replicating " + call);
161       // voila, invalidated!
162
replicateCall(call, configuration.getCacheMode() == Configuration.CacheMode.INVALIDATION_SYNC);
163    }
164
165    protected void invalidateModifications(List JavaDoc<MethodCall> modifications, TransactionWorkspace workspace) throws Throwable JavaDoc
166    {
167       // optimise the calls list here.
168
Set JavaDoc<Fqn> modifiedFqns = optimisedIterator(modifications);
169       for (Fqn fqn : modifiedFqns) invalidateAcrossCluster(fqn, workspace);
170    }
171
172    protected TransactionWorkspace getWorkspace(GlobalTransaction gtx)
173    {
174       OptimisticTransactionEntry entry = (OptimisticTransactionEntry) txTable.get(gtx);
175       return entry.getTransactionWorkSpace();
176    }
177
178    protected Fqn findFqn(Object JavaDoc[] objects)
179    {
180       // it *should* be the 2nd param...
181
return (Fqn) objects[1];
182    }
183
184    /**
185     * Removes non-crud methods, plus clobs together common calls to Fqn's.
186     * E.g, if we have put("/a/b", "1", "2") followed by a put("/a/b", "3",
187     * "4") we should only evict "/a/b" once.
188     *
189     * @param list
190     * @return Iterator containing a unique set of Fqns of crud methods in this tx
191     */

192    protected Set JavaDoc<Fqn> optimisedIterator(List JavaDoc<MethodCall> list)
193    {
194       Set JavaDoc<Fqn> fqns = new HashSet JavaDoc<Fqn>();
195       for (MethodCall mc : list)
196       {
197          if (MethodDeclarations.isCrudMethod(mc.getMethodId()))
198          {
199             fqns.add(findFqn(mc.getArgs()));
200          }
201       }
202       return fqns;
203    }
204 }
205
Popular Tags