KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > TreeCache


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.cache;
8
9 import org.jboss.cache.eviction.LRUPolicy;
10 import org.jboss.cache.interceptors.Interceptor;
11 import org.jboss.cache.loader.CacheLoader;
12 import org.jboss.cache.lock.IsolationLevel;
13 import org.jboss.cache.lock.LockStrategyFactory;
14 import org.jboss.cache.lock.LockingException;
15 import org.jboss.cache.lock.TimeoutException;
16 import org.jboss.logging.Logger;
17 import org.jboss.system.ServiceMBeanSupport;
18 import org.jboss.util.NestedRuntimeException;
19 import org.jgroups.*;
20 import org.jgroups.blocks.GroupRequest;
21 import org.jgroups.blocks.MethodCall;
22 import org.jgroups.blocks.RpcDispatcher;
23 import org.jgroups.util.Rsp;
24 import org.jgroups.util.RspList;
25 import org.jgroups.util.Util;
26 import org.w3c.dom.Attr JavaDoc;
27 import org.w3c.dom.Element JavaDoc;
28 import org.w3c.dom.NamedNodeMap JavaDoc;
29 import org.w3c.dom.NodeList JavaDoc;
30
31 import javax.transaction.Status JavaDoc;
32 import javax.transaction.SystemException JavaDoc;
33 import javax.transaction.Transaction JavaDoc;
34 import javax.transaction.TransactionManager JavaDoc;
35 import java.lang.reflect.Method JavaDoc;
36 import java.util.*;
37
38 /**
39  * A tree-like structure that is replicated across several members. Updates will
40  * be multicast to all group members reliably and in the same order. User has the
41  * option to set transaction isolation level now (e.g., <code>SERIALIZABLE</code>, or
42  * <code>REPEATABLE_READ</code>.
43  *
44  * @author Bela Ban
45  * @author Ben Wang
46  * @version $Id: TreeCache.java,v 1.158.2.6 2005/04/04 05:44:13 bwang00 Exp $
47  * @jmx.mbean extends="org.jboss.system.ServiceMBean"
48  * <p/>
49  */

50 public class TreeCache extends ServiceMBeanSupport implements TreeCacheMBean, Cloneable JavaDoc, MembershipListener {
51    protected Node root=new Node(SEPARATOR, Fqn.fromString(SEPARATOR), null, null, this);
52    protected final Vector listeners=new Vector();
53    protected JChannel channel=null;
54
55    /** Am I the coordinator ? */
56    protected boolean coordinator=false;
57
58    protected String JavaDoc cluster_name="TreeCache-Group";
59    protected String JavaDoc cluster_props=null;
60    protected final Vector members=new Vector();
61    protected RpcDispatcher disp=null;
62    protected MessageListener ml=new MessageListenerAdaptor(this, log);
63    protected long state_fetch_timeout=5000;
64    protected long sync_repl_timeout=15000;
65    protected boolean use_repl_queue=false;
66    protected int repl_queue_max_elements=1000;
67    protected long repl_queue_interval=5000;
68
69    /** Maintains mapping of transactions (keys) and Modifications/Undo-Operations */
70    private final TransactionTable tx_table=new TransactionTable();
71
72    /** HashMap<Thread, List<Lock>, maintains locks acquired by threads (used when no TXs are used) */
73    private final HashMap lock_table=new HashMap();
74
75    protected boolean fetch_state_on_startup=true;
76    protected long lock_acquisition_timeout=10000;
77    protected String JavaDoc eviction_policy_class=null;
78    protected TreeCacheListener eviction_policy_provider = null;
79    protected int cache_mode=LOCAL;
80
81
82    public static Method JavaDoc putDataMethodLocal=null;
83    public static Method JavaDoc putDataEraseMethodLocal=null;
84    public static Method JavaDoc putKeyValMethodLocal=null;
85    public static Method JavaDoc putFailFastKeyValueMethodLocal=null;
86    public static Method JavaDoc removeNodeMethodLocal=null;
87    public static Method JavaDoc removeKeyMethodLocal=null;
88    public static Method JavaDoc removeDataMethodLocal=null;
89    public static Method JavaDoc evictNodeMethodLocal=null;
90    // public static Method evictKeyValueMethodLocal=null;
91
public static Method JavaDoc prepareMethod=null;
92    public static Method JavaDoc commitMethod=null;
93    public static Method JavaDoc rollbackMethod=null;
94    public static Method JavaDoc replicateMethod=null;
95    public static Method JavaDoc replicateAllMethod=null;
96    // public static Method addChildMethod=null;
97
public static Method JavaDoc addChildMethodLocal=null;
98    public static Method JavaDoc getKeyValueMethodLocal=null;
99    public static Method JavaDoc getNodeMethodLocal=null;
100    public static Method JavaDoc getKeysMethodLocal=null;
101    public static Method JavaDoc getChildrenNamesMethodLocal=null;
102    public static Method JavaDoc releaseAllLocksMethodLocal=null;
103    public static Method JavaDoc printMethodLocal=null;
104    public static Method JavaDoc lockMethodLocal=null;
105
106    static LinkedList crud_methods=new LinkedList();
107    protected boolean isStateSet=false;
108    private final Object JavaDoc stateLock=new Object JavaDoc();
109    protected IsolationLevel isolationLevel=IsolationLevel.REPEATABLE_READ;
110
111    /** Eviction policy configuration in xml Element */
112    protected Element JavaDoc evictConfig_ = null;
113
114
115
116    public MessageListener getMessageListener() {
117       return ml;
118    }
119
120    /** {@link #invokeMethod(MethodCall)} will dispatch to this chain of interceptors.
121     * In the future, this will be replaced with JBossAop. This is a first step towards refactoring JBossCache.
122     */

123    protected Interceptor interceptor_chain=null;
124
125
126    /**
127     * Interceptor which handles invocations of {@link #_replicate(MethodCall)}. Any such method
128     * invocation is forwarded to the invoke_handler.<br/>
129     * This will go away in the future, as we're moving replication functionality into the
130     * ReplicationInterceptor itself
131     */

132    protected Replicatable replication_handler=null;
133
134
135    /** Method to acquire a TransactionManager. By default we use JBossTransactionManagerLookup. Has
136     * to be set before calling {@link #start()} */

137    protected TransactionManagerLookup tm_lookup=null;
138
139    /** Class of the implementation of TransactionManagerLookup */
140    protected String JavaDoc tm_lookup_class=null;
141
142    /** Used to get the Transaction associated with the current thread */
143    protected TransactionManager JavaDoc tm=null;
144
145    /** The fully qualified name of the CacheLoader (has to implement the CacheLoader interface) */
146    protected String JavaDoc cache_loader_class=null;
147
148    /** A reference to the CacheLoader. If null, we don't have a CachedLoader */
149    protected CacheLoader cache_loader=null;
150
151    /** The properties from which to configure the CacheLoader */
152    protected Properties cache_loader_config=null;
153
154    /** Are the CacheLoaders sharing the same resource or not ? */
155    protected boolean cache_loader_shared=true;
156
157    /** List<Fqn> of nodes to preload (if cache loader is enabled) */
158    protected List cache_loader_preload=null;
159
160    /** Fetches the transient state. Attribute fetch_cache_on_startup has to be true */
161    protected boolean cache_loader_fetch_transient_state=true;
162
163    /** Fetches the entire persistent state from the underlying CacheLoader. Only used if cache_loader_shared=false.
164     * Attribute fetch_cache_on_startup has to be true */

165    protected boolean cache_loader_fetch_persistent_state=true;
166
167    /** synchronous or asynchrous commit phase ? */
168    protected boolean sync_commit_phase=false;
169
170    /** synchronous or asynchrous rollback phase ? */
171    protected boolean sync_rollback_phase=false;
172
173    protected boolean deadlockDetection=false;
174
175    /** Queue used to replicate updates when mode is repl-async */
176    protected ReplicationQueue repl_queue=null;
177
178    public static final String JavaDoc SEPARATOR="/";
179
180    /** Entries in the cache are by default local; ie. not replicated */
181    public static final int LOCAL=1;
182
183    /** Entries in the cache are by default replicated (asynchronously) */
184    public static final int REPL_ASYNC=2;
185
186    /** Entries in the cache are by default replicated (synchronously) */
187    public static final int REPL_SYNC=3;
188
189    static public final String JavaDoc UNINITIALIZED="jboss:internal:uninitialized"; // todo: move to CacheLoaderInterceptor
190
static final String JavaDoc JNDI_LOCATOR_URI="socket://localhost:6789";
191
192
193    static {
194       try {
195          putDataMethodLocal=TreeCache.class.getDeclaredMethod("_put",
196                                                    new Class JavaDoc[]{GlobalTransaction.class,
197                                                                Fqn.class,
198                                                                Map JavaDoc.class,
199                                                                boolean.class});
200          putDataEraseMethodLocal=TreeCache.class.getDeclaredMethod("_put",
201                                                          new Class JavaDoc[]{GlobalTransaction.class,
202                                                                      Fqn.class,
203                                                                      Map JavaDoc.class,
204                                                                      boolean.class,
205                                                                      boolean.class});
206          putKeyValMethodLocal=TreeCache.class.getDeclaredMethod("_put",
207                                                       new Class JavaDoc[]{GlobalTransaction.class,
208                                                                   Fqn.class,
209                                                                   Object JavaDoc.class,
210                                                                   Object JavaDoc.class,
211                                                                   boolean.class});
212          putFailFastKeyValueMethodLocal=TreeCache.class.getDeclaredMethod("_put",
213                                                                           new Class JavaDoc[]{GlobalTransaction.class,
214                                                                           Fqn.class,
215                                                                           Object JavaDoc.class,
216                                                                           Object JavaDoc.class,
217                                                                           boolean.class,
218                                                                           long.class});
219          removeNodeMethodLocal=TreeCache.class.getDeclaredMethod("_remove",
220                                                       new Class JavaDoc[]{GlobalTransaction.class,
221                                                                   Fqn.class,
222                                                                   boolean.class});
223          removeKeyMethodLocal=TreeCache.class.getDeclaredMethod("_remove",
224                                                      new Class JavaDoc[]{GlobalTransaction.class,
225                                                                  Fqn.class,
226                                                                  Object JavaDoc.class,
227                                                                  boolean.class});
228          removeDataMethodLocal=TreeCache.class.getDeclaredMethod("_removeData",
229                                                       new Class JavaDoc[]{GlobalTransaction.class,
230                                                                   Fqn.class,
231                                                                   boolean.class});
232          evictNodeMethodLocal=TreeCache.class.getDeclaredMethod("_evict", new Class JavaDoc[] {Fqn.class});
233          // evictKeyValueMethodLocal=TreeCache.class.getDeclaredMethod("_evict", new Class[]{Fqn.class, Object.class});
234
prepareMethod=TreeCache.class.getDeclaredMethod("prepare",
235                                                   new Class JavaDoc[]{GlobalTransaction.class,
236                                                               List.class,
237                                                               Address.class,
238                                                               boolean.class});
239          commitMethod=TreeCache.class.getDeclaredMethod("commit",
240                                                  new Class JavaDoc[]{GlobalTransaction.class});
241          rollbackMethod=TreeCache.class.getDeclaredMethod("rollback",
242                                                    new Class JavaDoc[]{GlobalTransaction.class});
243          addChildMethodLocal=TreeCache.class.getDeclaredMethod("_addChild",
244                                                    new Class JavaDoc[]{GlobalTransaction.class,
245                                                                Fqn.class, Object JavaDoc.class, Node.class});
246          getKeyValueMethodLocal=TreeCache.class.getDeclaredMethod("_get",
247                                                                   new Class JavaDoc[]{Fqn.class, Object JavaDoc.class, boolean.class});
248          getNodeMethodLocal=TreeCache.class.getDeclaredMethod("_get", new Class JavaDoc[]{Fqn.class});
249          getKeysMethodLocal=TreeCache.class.getDeclaredMethod("_getKeys", new Class JavaDoc[]{Fqn.class});
250          getChildrenNamesMethodLocal=TreeCache.class.getDeclaredMethod("_getChildrenNames", new Class JavaDoc[]{Fqn.class});
251          replicateMethod=TreeCache.class.getDeclaredMethod("_replicate", new Class JavaDoc[]{MethodCall.class});
252          replicateAllMethod=TreeCache.class.getDeclaredMethod("_replicate", new Class JavaDoc[]{List.class});
253          releaseAllLocksMethodLocal=TreeCache.class.getDeclaredMethod("_releaseAllLocks", new Class JavaDoc[]{Fqn.class});
254          printMethodLocal=TreeCache.class.getDeclaredMethod("_print", new Class JavaDoc[]{Fqn.class});
255          lockMethodLocal=TreeCache.class.getDeclaredMethod("_lock", new Class JavaDoc[]{Fqn.class,
256                                                                                 int.class,
257                                                                                 boolean.class});
258       }
259       catch(NoSuchMethodException JavaDoc ex) {
260          ex.printStackTrace();
261          throw new ExceptionInInitializerError JavaDoc(ex.toString());
262       }
263
264       crud_methods.add(putDataMethodLocal);
265       crud_methods.add(putDataEraseMethodLocal);
266       crud_methods.add(putKeyValMethodLocal);
267       crud_methods.add(putFailFastKeyValueMethodLocal);
268       crud_methods.add(removeNodeMethodLocal);
269       crud_methods.add(removeKeyMethodLocal);
270       crud_methods.add(removeDataMethodLocal);
271    }
272
273
274
275
276    public static boolean isCrudMethod(Method JavaDoc m) {
277       return m == null? false : crud_methods.contains(m);
278    }
279
280
281
282    /**
283     * Creates a channel with the given properties. Connects to the channel, then creates a PullPushAdapter
284     * and starts it
285     */

286    public TreeCache(String JavaDoc cluster_name, String JavaDoc props, long state_fetch_timeout) throws Exception JavaDoc {
287       super();
288       if(cluster_name != null)
289          this.cluster_name=cluster_name;
290       if(props != null)
291          this.cluster_props=props;
292       this.state_fetch_timeout=state_fetch_timeout;
293    }
294
295    public TreeCache() throws Exception JavaDoc {
296       super();
297 // try {
298
// Naming.rebind("//localhost:1098/" + this.getClusterName(), new RemoteTreeCacheImpl(this));
299
// }
300
// catch(Throwable t) {
301
// log.error("Unable to bind remote tree cache implementation to '" + this.getClusterName() + "'.", t);
302
// }
303
}
304
305    /**
306     * Expects an already connected channel. Creates a PullPushAdapter and starts it
307     */

308    public TreeCache(JChannel channel) throws Exception JavaDoc {
309       super();
310       this.channel=channel;
311    }
312
313    /**
314     * Used by interceptors. Don't use as client, will go away: interceptors will use TreeCacheImpl and clients
315     * will only be able to use TreeCache (which will become an interface)
316     * @return
317     */

318    public Node getRoot() {
319       return root;
320    }
321
322    /**
323     * @return
324     * @jmx.managed-attribute access="read-only"
325     */

326    public Object JavaDoc getLocalAddress() {
327       return channel != null ? channel.getLocalAddress() : null;
328    }
329
330    /**
331     * @return
332     * @jmx.managed-attribute access="read-only"
333     */

334    public Vector getMembers() {
335       return members;
336    }
337
338    /**
339     *
340     * @return
341     * @jmx.managed-attribute
342     */

343    public boolean isCoordinator() {
344       return coordinator;
345    }
346
347    /**
348     * Get the name of the replication group
349     * @jmx.managed-attribute
350     */

351    public String JavaDoc getClusterName() {
352       return cluster_name;
353    }
354
355    /**
356     * Set the name of the replication group
357     * @jmx.managed-attribute
358     */

359    public void setClusterName(String JavaDoc name) {
360       cluster_name=name;
361    }
362
363    /**
364     * Get the cluster properties (e.g. the protocol stack specification in case of JGroups)
365     * @jmx.managed-attribute
366     */

367    public String JavaDoc getClusterProperties() {
368       return cluster_props;
369    }
370
371    /**
372     * Set the cluster properties. If the cache is to use the new properties, it has to be redeployed
373     * @param cluster_props The properties for the cluster (JGroups)
374     * @jmx.managed-attribute
375     */

376    public void setClusterProperties(String JavaDoc cluster_props) {
377       this.cluster_props=cluster_props;
378    }
379
380
381    public TransactionTable getTransactionTable() {
382       return tx_table;
383    }
384
385
386    public HashMap getLockTable() {
387       return lock_table;
388    }
389
390    /**
391     * Dumps the contents of the TransactionTable
392     * @return
393     * @jmx.managed-attribute
394     */

395    public String JavaDoc dumpTransactionTable() {
396       return tx_table.toString(true);
397    }
398
399    /**
400     *
401     * @return
402     * @jmx.managed-attribute
403     */

404    public boolean getDeadlockDetection() {
405       return deadlockDetection;
406    }
407
408    /**
409     *
410     * @param dt
411     * @jmx.managed-attribute
412     */

413    public void setDeadlockDetection(boolean dt) {
414       deadlockDetection=dt;
415       if(disp != null)
416          disp.setDeadlockDetection(dt);
417    }
418
419    /**
420     * @return
421     * @jmx.managed-attribute
422     */

423    public String JavaDoc getInterceptorChain() {
424       String JavaDoc retval=printInterceptorChain(interceptor_chain);
425       if(retval == null || retval.length() == 0)
426          return "<empty>";
427       else
428          return retval;
429    }
430
431
432    /**
433     * @return List<Interceptor>
434     * @jmx.managed-attribute
435     */

436    public List getInterceptors() {
437       if(interceptor_chain == null)
438          return null;
439       int num=1;
440       Interceptor tmp=interceptor_chain;
441       while((tmp=tmp.getNext()) != null) {
442          num++;
443       }
444       List retval=new ArrayList(num);
445       tmp=interceptor_chain;
446       num=0;
447       do {
448          retval.add(tmp);
449          tmp=tmp.getNext();
450       }
451       while(tmp != null);
452       return retval;
453    }
454
455
456
457
458    /**
459     * @return
460     * @jmx.managed-attribute
461     */

462    public String JavaDoc getCacheLoaderClass() {
463       return cache_loader_class;
464    }
465
466    /**
467     *
468     * @param cache_loader_class
469     * @jmx.managed-attribute
470     */

471    public void setCacheLoaderClass(String JavaDoc cache_loader_class) {
472       this.cache_loader_class=cache_loader_class;
473    }
474
475    /**
476     * @return
477     * @jmx.managed-attribute
478     */

479    public Properties getCacheLoaderConfig() {
480       return cache_loader_config;
481    }
482
483    /**
484     * @param cache_loader_config
485     * @jmx.managed-attribute
486     */

487    public void setCacheLoaderConfig(Properties cache_loader_config) {
488       this.cache_loader_config=cache_loader_config;
489    }
490
491    /**
492     * @return
493     * @jmx.managed-attribute
494     */

495    public CacheLoader getCacheLoader() {
496       return cache_loader;
497    }
498
499    /**
500     * @param cache_loader
501     * @jmx.managed-attribute
502     */

503    public void setCacheLoader(CacheLoader cache_loader) {
504       this.cache_loader=cache_loader;
505    }
506
507    /**
508     * @return
509     * @jmx.managed-attribute
510     */

511    public boolean getCacheLoaderShared() {
512       return cache_loader_shared;
513    }
514
515    /**
516     * @param shared
517     * @jmx.managed-attribute
518     */

519    public void setCacheLoaderShared(boolean shared) {
520       this.cache_loader_shared=shared;
521    }
522
523
524    /**
525     * @param list
526     * @jmx.managed-attribute
527     */

528    public void setCacheLoaderPreload(String JavaDoc list) {
529       if(list == null) return;
530       ArrayList l;
531       StringTokenizer st=new StringTokenizer(list, ",");
532       String JavaDoc tok;
533       Fqn fqn;
534       l=new ArrayList();
535       while(st.hasMoreTokens()) {
536          tok=st.nextToken();
537          fqn=Fqn.fromString(tok.trim());
538          l.add(fqn);
539       }
540       if(l.size() > 0)
541          this.cache_loader_preload=l;
542    }
543
544
545
546    /**
547     * @return
548     * @jmx.managed-attribute
549     */

550    public String JavaDoc getCacheLoaderPreload() {
551       return cache_loader_preload != null? cache_loader_preload.toString() : null;
552    }
553
554    /**
555     * @param flag
556     * @jmx.managed-attribute
557     */

558    public void setCacheLoaderFetchPersistentState(boolean flag) {
559       cache_loader_fetch_persistent_state=flag;
560    }
561
562    /**
563     * @return
564     * @jmx.managed-attribute
565     */

566    public boolean getCacheLoaderFetchPersistentState() {
567       return cache_loader_fetch_persistent_state;
568    }
569
570    /**
571     * @param flag
572     * @jmx.managed-attribute
573     */

574    public void setCacheLoaderFetchTransientState(boolean flag) {
575       cache_loader_fetch_transient_state=flag;
576    }
577
578    /**
579     * @return
580     * @jmx.managed-attribute
581     */

582    public boolean getCacheLoaderFetchTransientState() {
583       return cache_loader_fetch_transient_state;
584    }
585
586
587    /**
588     * @return
589     * @jmx.managed-attribute
590     */

591    public boolean getSyncCommitPhase() {
592       return sync_commit_phase;
593    }
594
595    /**
596     * @param sync_commit_phase
597     * @jmx.managed-attribute
598     */

599    public void setSyncCommitPhase(boolean sync_commit_phase) {
600       this.sync_commit_phase=sync_commit_phase;
601    }
602
603    /**
604     * @return
605     * @jmx.managed-attribute
606     */

607    public boolean getSyncRollbackPhase() {
608       return sync_rollback_phase;
609    }
610
611    /**
612     * @param sync_rollback_phase
613     * @jmx.managed-attribute
614     */

615    public void setSyncRollbackPhase(boolean sync_rollback_phase) {
616       this.sync_rollback_phase=sync_rollback_phase;
617    }
618
619
620    /**
621     * Setup eviction policy configuration
622     * @jmx.managed-attribute access="write-only"
623     */

624    public void setEvictionPolicyConfig(Element JavaDoc config) {
625       evictConfig_ = config;
626       log.info("setEvictionPolicyConfig(): " +config);
627    }
628
629    public Element JavaDoc getEvictionPolicyConfig() {
630       return evictConfig_;
631    }
632
633    /**
634     * Convert a list of elements to the JG property string
635     * @jmx.managed-attribute access="write-only"
636     */

637    public void setClusterConfig(Element JavaDoc config) {
638       StringBuffer JavaDoc buffer=new StringBuffer JavaDoc();
639       NodeList JavaDoc stack=config.getChildNodes();
640       int length=stack.getLength();
641
642       for(int s=0; s < length; s++) {
643          org.w3c.dom.Node JavaDoc node=stack.item(s);
644          if(node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE)
645             continue;
646
647          Element JavaDoc tag=(Element JavaDoc)node;
648          String JavaDoc protocol=tag.getTagName();
649          buffer.append(protocol);
650          NamedNodeMap JavaDoc attrs=tag.getAttributes();
651          int attrLength=attrs.getLength();
652          if(attrLength > 0)
653             buffer.append('(');
654          for(int a=0; a < attrLength; a++) {
655             Attr JavaDoc attr=(Attr JavaDoc)attrs.item(a);
656             String JavaDoc name=attr.getName();
657             String JavaDoc value=attr.getValue();
658             buffer.append(name);
659             buffer.append('=');
660             buffer.append(value);
661             if(a < attrLength - 1)
662                buffer.append(';');
663          }
664          if(attrLength > 0)
665             buffer.append(')');
666          buffer.append(':');
667       }
668       // Remove the trailing ':'
669
buffer.setLength(buffer.length() - 1);
670       setClusterProperties(buffer.toString());
671       log.info("setting cluster properties from xml to: " + cluster_props);
672    }
673
674
675
676
677    /**
678     * Get the max time to wait until the initial state is retrieved.
679     * This is used in a replicating cache: when a new cache joins the cluster,
680     * it needs to acquire the (replicated) state of the other members to
681     * initialize itself. If no state has been received within <tt>timeout</tt>
682     * milliseconds, the map will be empty.
683     *
684     * @return long Number of milliseconds to wait for the state. 0 means to wait forever.
685     * @jmx.managed-attribute
686     */

687    public long getInitialStateRetrievalTimeout() {
688       return state_fetch_timeout;
689    }
690
691    /**
692     * Set the initial state transfer timeout
693     * (see {@link #getInitialStateRetrievalTimeout()})
694     * @jmx.managed-attribute
695     */

696    public void setInitialStateRetrievalTimeout(long timeout) {
697       state_fetch_timeout=timeout;
698    }
699
700    /**
701     * Returns the current caching mode. Valid values are
702     * <ul>
703     * <li>LOCAL
704     * <li>REPL_ASYNC
705     * <li>REPL_SYNC
706     * <ul>
707     * @return String The caching mode
708     * @jmx.managed-attribute
709     */

710    public String JavaDoc getCacheMode() {
711       return mode2String(cache_mode);
712    }
713
714    public int getCacheModeInternal() {
715       return cache_mode;
716    }
717
718    private String JavaDoc mode2String(int mode) {
719       switch(mode) {
720          case LOCAL:
721             return "LOCAL";
722          case REPL_ASYNC:
723             return "REPL_ASYNC";
724          case REPL_SYNC:
725             return "REPL_SYNC";
726          default:
727             throw new RuntimeException JavaDoc("setCacheMode(): caching mode " + mode + " is invalid");
728       }
729    }
730
731    /**
732     * Sets the default caching mode)
733     * @jmx.managed-attribute
734     */

735    public void setCacheMode(String JavaDoc mode) throws Exception JavaDoc {
736       int m=string2Mode(mode);
737       setCacheMode(m);
738    }
739
740
741    /**
742     * Sets the default cache mode. Valid arguments are
743     * <ol>
744     * <li>TreeCache.LOCAL
745     * <li>TreeCache.REPL_ASYNC
746     * <li>TreeCache.REPL_SYNC
747     * </ol>
748     * @param mode
749     */

750    public void setCacheMode(int mode) {
751       if(mode == LOCAL || mode == REPL_ASYNC || mode == REPL_SYNC)
752          this.cache_mode=mode;
753       else
754          throw new IllegalArgumentException JavaDoc("setCacheMode(): caching mode " + mode + " is invalid");
755    }
756
757
758    /**
759     * Returns the default max timeout after which synchronous replication calls return.
760     * @return long Number of milliseconds after which a sync repl call must return. 0 means to wait forever
761     * @jmx.managed-attribute
762     */

763    public long getSyncReplTimeout() {
764       return sync_repl_timeout;
765    }
766
767    /**
768     * Sets the default maximum wait time for synchronous replication to receive all results
769     * @jmx.managed-attribute
770     */

771    public void setSyncReplTimeout(long timeout) {
772       sync_repl_timeout=timeout;
773    }
774
775    /**
776     * @return
777     * @jmx.managed-attribute
778     */

779    public boolean getUseReplQueue() {
780       return use_repl_queue;
781    }
782
783    /**
784     * @param flag
785     * @jmx.managed-attribute
786     */

787    public void setUseReplQueue(boolean flag) {
788       use_repl_queue=flag;
789       if(flag) {
790          if(repl_queue == null) {
791             repl_queue=new ReplicationQueue(this, repl_queue_interval, repl_queue_max_elements);
792             if(repl_queue_interval >= 0)
793                repl_queue.start();
794          }
795       }
796       else {
797          if(repl_queue != null) {
798             repl_queue.stop();
799             repl_queue=null;
800          }
801       }
802    }
803
804    /**
805     * @return
806     * @jmx.managed-attribute
807     */

808    public long getReplQueueInterval() {
809       return repl_queue_interval;
810    }
811
812    /**
813     * @param interval
814     * @jmx.managed-attribute
815     */

816    public void setReplQueueInterval(long interval) {
817       this.repl_queue_interval=interval;
818       if(repl_queue != null)
819          repl_queue.setInterval(interval);
820    }
821
822    /**
823     * @return
824     * @jmx.managed-attribute
825     */

826    public int getReplQueueMaxElements() {
827       return repl_queue_max_elements;
828    }
829
830    /**
831     * @param max_elements
832     * @jmx.managed-attribute
833     */

834    public void setReplQueueMaxElements(int max_elements) {
835       this.repl_queue_max_elements=max_elements;
836       if(repl_queue != null)
837          repl_queue.setMax_elements(max_elements);
838    }
839
840
841    public ReplicationQueue getReplQueue() {
842       return repl_queue;
843    }
844
845    /**
846     * Returns the transaction isolation level.
847     * @jmx.managed-attribute
848     */

849    public String JavaDoc getIsolationLevel() {
850       return isolationLevel.toString();
851    }
852
853    /**
854     * Set the transaction isolation level. This determines the locking strategy to be used
855     * @jmx.managed-attribute
856     */

857    public void setIsolationLevel(String JavaDoc level) {
858       IsolationLevel tmp_level=IsolationLevel.stringToIsolationLevel(level);
859
860       if(tmp_level == null) {
861          throw new IllegalArgumentException JavaDoc("TreeCache.setIsolationLevel(): level \"" + level + "\" is invalid");
862       }
863       setIsolationLevel(tmp_level);
864    }
865
866    /**
867     * @param level
868     */

869    public void setIsolationLevel(IsolationLevel level) {
870       isolationLevel=level;
871       LockStrategyFactory.setIsolationLevel(level);
872    }
873
874    public IsolationLevel getIsolationLevelClass() {
875       return isolationLevel;
876    }
877
878    /**
879     * @return
880     * @jmx.managed-attribute
881     */

882    public boolean getFetchStateOnStartup() {
883       return fetch_state_on_startup;
884    }
885
886
887    /**
888     * @param flag
889     * @jmx.managed-attribute
890     */

891    public void setFetchStateOnStartup(boolean flag) {
892       fetch_state_on_startup=flag;
893    }
894
895
896    /**
897     * Default max time to wait for a lock. If the lock cannot be acquired within this time, a LockingException will be thrown.
898     * @return long Max number of milliseconds to wait for a lock to be acquired
899     * @jmx.managed-attribute
900     */

901    public long getLockAcquisitionTimeout() {
902       return lock_acquisition_timeout;
903    }
904
905    /**
906     * Set the max time for lock acquisition. A value of 0 means to wait forever (not recomended).
907     * Note that lock acquisition timeouts may be removed in the future when we have deadlock detection.
908     * @param timeout
909     * @jmx.managed-attribute
910     */

911    public void setLockAcquisitionTimeout(long timeout) {
912       this.lock_acquisition_timeout=timeout;
913    }
914
915
916
917    /**
918     * Returns the name of the cache eviction policy (must be an implementation of EvictionPolicy)
919     * @return Fully qualified name of a class implementing the EvictionPolicy interface
920     * @jmx.managed-attribute
921     */

922    public String JavaDoc getEvictionPolicyClass() {
923       return eviction_policy_class;
924    }
925
926    /**
927     * Sets the classname of the eviction policy
928     * @jmx.managed-attribute
929     */

930    public void setEvictionPolicyClass(String JavaDoc eviction_policy_class) {
931       if(eviction_policy_class == null || eviction_policy_class.length() ==0)
932          return;
933       try {
934          this.eviction_policy_class=eviction_policy_class;
935          eviction_policy_provider =(TreeCacheListener)
936                getClass().getClassLoader().loadClass(eviction_policy_class).newInstance();
937          this.addTreeCacheListener(eviction_policy_provider );
938       }
939       catch(Throwable JavaDoc t) {
940          log.error("setEvictionPolicyClass(): failed creating instance of " + eviction_policy_class, t);
941       }
942    }
943
944    /**
945     * Obtain eviction thread (if any) wake up interval in seconds
946     * @jmx.managed-attribute
947     */

948    public int getEvictionThreadWakeupIntervalSeconds() {
949       if( eviction_policy_provider == null ) return -1;
950       else
951          return ((LRUPolicy)eviction_policy_provider).getWakeupIntervalSeconds();
952    }
953
954
955    /**
956     * Sets the TransactionManagerLookup object
957     * @param l
958     * @jmx.managed-attribute
959     */

960    public void setTransactionManagerLookup(TransactionManagerLookup l) {
961       this.tm_lookup=l;
962    }
963
964
965    /**
966     * @return
967     * @jmx.managed-attribute
968     */

969    public String JavaDoc getTransactionManagerLookupClass() {
970       return tm_lookup_class;
971    }
972
973    /**
974     * Sets the class of the TransactionManagerLookup impl. This will attempt to create an
975     * instance, and will throw an exception if this fails.
976     * @param cl
977     * @throws Exception
978     * @jmx.managed-attribute
979     */

980    public void setTransactionManagerLookupClass(String JavaDoc cl) throws Exception JavaDoc {
981       this.tm_lookup_class=cl;
982    }
983
984    /**
985     * @return
986     * @jmx.managed-attribute
987     */

988    public TransactionManager JavaDoc getTransactionManager() {
989       return tm;
990    }
991
992    /**
993     * @return
994     * @jmx.managed-attribute
995     */

996    public TreeCache getInstance() {
997       return this;
998    }
999
1000
1001
1002   public void setReplicationHandler(Replicatable handler) {
1003      replication_handler=handler;
1004   }
1005
1006   public Replicatable getReplicationHandler() {
1007      return replication_handler;
1008   }
1009
1010   /**
1011    * Fetch the group state from the current coordinator. If successful, this will trigger setState().
1012    * @jmx.managed-operation
1013    */

1014   public void fetchState(long timeout) throws ChannelClosedException, ChannelNotConnectedException {
1015      if(channel == null)
1016         throw new ChannelNotConnectedException();
1017      boolean rc=channel.getState(null, timeout);
1018      if(rc)
1019         log.info("fetchState(): state was retrieved successfully");
1020      else
1021         log.info("fetchState(): state could not be retrieved (first member)");
1022   }
1023
1024   /**
1025    * @param listener
1026    * @jmx.managed-operation
1027    */

1028   public void addTreeCacheListener(TreeCacheListener listener) {
1029      if(!listeners.contains(listener))
1030         listeners.addElement(listener);
1031   }
1032
1033   /**
1034    * @param listener
1035    * @jmx.managed-operation
1036    */

1037   public void removeTreeCacheListener(TreeCacheListener listener) {
1038      listeners.removeElement(listener);
1039   }
1040
1041   /* --------------------------- MBeanSupport ------------------------- */
1042
1043   /**
1044    *
1045    * @throws Exception
1046    * @jmx.managed-operation
1047    */

1048   public void createService() throws Exception JavaDoc {
1049   }
1050
1051   /**
1052    * @jmx.managed-operation
1053    */

1054   public void destroyService() {
1055   }
1056
1057
1058   /**
1059    *
1060    * @throws Exception
1061    * @jmx.managed-operation
1062    */

1063   public void startService() throws Exception JavaDoc {
1064      if(this.tm_lookup == null && this.tm_lookup_class != null) {
1065         Class JavaDoc clazz=Thread.currentThread().getContextClassLoader().loadClass(this.tm_lookup_class);
1066         this.tm_lookup=(TransactionManagerLookup)clazz.newInstance();
1067      }
1068
1069      try {
1070         if(tm_lookup != null)
1071            tm=tm_lookup.getTransactionManager();
1072         else
1073            log.warn("No transaction manager lookup class has been defined. Transactions cannot be used");
1074      }
1075      catch(Exception JavaDoc e) {
1076         log.debug("failed looking up TransactionManager, will not use transactions", e);
1077      }
1078
1079      // Create the cache loader (needs to be created regardless of mode (local/replicated). This method will
1080
// be a no-op if no cache loader has been configured
1081
createCacheLoader();
1082
1083      // Create the interceptors in the correct order (later to be defined in XML file)
1084
createInterceptorChain();
1085
1086      createEvictionPolicy();
1087
1088      switch(cache_mode) {
1089         case LOCAL:
1090            log.info("cache mode is local, will not create the channel");
1091            break;
1092         case REPL_SYNC:
1093         case REPL_ASYNC:
1094            log.info("cache mode is " + mode2String(cache_mode));
1095            if(channel != null) { // already started
1096
log.info("channel is already running");
1097               return;
1098            }
1099            if(cluster_props == null) {
1100               cluster_props=getDefaultProperties();
1101               log.debug("setting cluster properties to default value");
1102            }
1103
1104            channel=new JChannel(cluster_props);
1105            channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE);
1106            channel.setOpt(Channel.AUTO_GETSTATE, Boolean.TRUE);
1107            if(log.isTraceEnabled())
1108               log.trace("cache properties: " + cluster_props);
1109            channel.setOpt(Channel.GET_STATE_EVENTS, Boolean.TRUE);
1110            disp=new RpcDispatcher(channel, ml, this, this);
1111            disp.setDeadlockDetection(deadlockDetection);
1112            channel.connect(cluster_name);
1113            if(fetch_state_on_startup) {
1114               fetchStateOnStartup();
1115            }
1116            break;
1117         default:
1118            throw new IllegalArgumentException JavaDoc("cache mode " + cache_mode + " is invalid");
1119      }
1120
1121      // does the preloading
1122
cacheLoaderPreload();
1123
1124      coordinator=determineCoordinator();
1125      notifyCacheStarted();
1126   }
1127
1128   private void createEvictionPolicy() {
1129      // Configure if eviction policy is set
1130
if(eviction_policy_provider != null)
1131         ((LRUPolicy)eviction_policy_provider).configure(this);
1132   }
1133
1134   /**
1135    * Assembles the interceptor stack. Presence and order of interceptors is determined by looking at
1136    * the cache configuration. In the future, this will be accessible through XML. See refactoring.txt for
1137    * details. An alternative might be to use a simple rools engine to assemble the stack.
1138    * Creates either:
1139    * <pre>
1140    *
1141    * CallInterceptor
1142    * LockInterceptor
1143    * [CacheLoaderInterceptor]
1144    * [ReplicationInterceptor]
1145    * [CacheStoreInterceptor]
1146    *
1147    * or
1148    *
1149    * CallInterceptor
1150    * LockInterceptor
1151    * [CacheStoreInterceptor]
1152    * [CacheLoaderInterceptor]
1153    * [ReplicationInterceptor]
1154    *
1155    * </pre>
1156    * CallInterceptor is always present at the top, the others may or may not be present
1157    */

1158   protected void createInterceptorChain() throws IllegalAccessException JavaDoc, InstantiationException JavaDoc, ClassNotFoundException JavaDoc {
1159      Interceptor call_interceptor=null;
1160      Interceptor lock_interceptor=null;
1161      // Interceptor create_if_not_exists_interceptor=null;
1162
Interceptor repl_interceptor=null;
1163      Interceptor cache_loader_interceptor=null;
1164      Interceptor cache_store_interceptor=null;
1165      Interceptor unlock_interceptor=null;
1166      Interceptor first=null;
1167
1168      call_interceptor=createInterceptor("org.jboss.cache.interceptors.CallInterceptor");
1169      call_interceptor.setCache(this);
1170
1171      lock_interceptor=createInterceptor("org.jboss.cache.interceptors.LockInterceptor");
1172      lock_interceptor.setCache(this);
1173
1174      //create_if_not_exists_interceptor=createInterceptor("org.jboss.cache.interceptors.CreateIfNotExistsInterceptor");
1175
//create_if_not_exists_interceptor.setCache(this);
1176

1177      unlock_interceptor=createInterceptor("org.jboss.cache.interceptors.UnlockInterceptor");
1178      unlock_interceptor.setCache(this);
1179
1180      if(cache_mode != LOCAL) {
1181         repl_interceptor=createInterceptor("org.jboss.cache.interceptors.ReplicationInterceptor");
1182         repl_interceptor.setCache(this);
1183      }
1184
1185      if(cache_loader_class != null || cache_loader != null) {
1186         cache_loader_interceptor=createInterceptor("org.jboss.cache.interceptors.CacheLoaderInterceptor");
1187         cache_loader_interceptor.setCache(this);
1188         cache_store_interceptor=createInterceptor("org.jboss.cache.interceptors.CacheStoreInterceptor");
1189         cache_store_interceptor.setCache(this);
1190      }
1191
1192
1193      // create the stack from the bottom up
1194
if(cache_loader_interceptor != null) {
1195         if(cache_loader_shared == true) {
1196            if(first == null)
1197               first=cache_store_interceptor;
1198            else
1199               addInterceptor(first, cache_store_interceptor);
1200         }
1201      }
1202
1203      if(repl_interceptor != null) {
1204         if(first == null)
1205            first=repl_interceptor;
1206         else
1207            addInterceptor(first, repl_interceptor);
1208      }
1209
1210      if(unlock_interceptor != null) {
1211         if(first == null)
1212            first=unlock_interceptor;
1213         else
1214            addInterceptor(first, unlock_interceptor);
1215      }
1216
1217      if(cache_loader_interceptor != null) {
1218         if(cache_loader_shared == true) {
1219            if(first == null)
1220               first=cache_loader_interceptor;
1221            else
1222               addInterceptor(first, cache_loader_interceptor);
1223         }
1224         else {
1225            if(first == null)
1226               first=cache_loader_interceptor;
1227            else
1228               addInterceptor(first, cache_loader_interceptor);
1229            if(first == null)
1230               first=cache_store_interceptor;
1231            else
1232               addInterceptor(first, cache_store_interceptor);
1233         }
1234      }
1235
1236      //if(first == null)
1237
// first=create_if_not_exists_interceptor;
1238
//else
1239
// addInterceptor(first, create_if_not_exists_interceptor);
1240

1241      if(first == null)
1242         first=lock_interceptor;
1243      else
1244         addInterceptor(first, lock_interceptor);
1245
1246      if(first == null)
1247         first=call_interceptor;
1248      else
1249         addInterceptor(first, call_interceptor);
1250
1251      interceptor_chain=first;
1252      if(log.isInfoEnabled())
1253         log.info("interceptor chain is:\n" + printInterceptorChain(first));
1254   }
1255
1256
1257
1258
1259   private String JavaDoc printInterceptorChain(Interceptor i) {
1260      StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
1261      if(i != null) {
1262         if(i.getNext() != null) {
1263            sb.append(printInterceptorChain(i.getNext())).append("\n");
1264         }
1265         sb.append(i.getClass());
1266      }
1267      return sb.toString();
1268   }
1269
1270
1271   /** Adds an interceptor at the end of the chain */
1272   private void addInterceptor(Interceptor first, Interceptor i) {
1273      if(first == null) {
1274         return;
1275      }
1276      do {
1277         if(first.getNext() != null)
1278            first=first.getNext();
1279         else
1280            break;
1281      }
1282      while(first != null); // findbugs has a false positive for an NPE here...
1283
first.setNext(i);
1284   }
1285
1286
1287   private Interceptor createInterceptor(String JavaDoc classname) throws ClassNotFoundException JavaDoc, IllegalAccessException JavaDoc, InstantiationException JavaDoc {
1288      Class JavaDoc clazz=getClass().getClassLoader().loadClass(classname);
1289      return (Interceptor)clazz.newInstance();
1290   }
1291
1292
1293//
1294

1295   /** Creates an instance of a CacheLoader if and only if
1296    * <ul>
1297    * <li>The CacheLoader has not yet been created
1298    * <li>cache_loader_class is set
1299    * <li>The CacheLoader is shared and we are <em>not</em> the coordinator (only the coordinator
1300    * is supposed to have a CacheLoader)
1301    * </ul>
1302    * @throws Exception
1303    */

1304   protected void createCacheLoader() throws Exception JavaDoc {
1305      if(cache_loader == null && cache_loader_class != null) {
1306         Class JavaDoc cl=Thread.currentThread().getContextClassLoader().loadClass(cache_loader_class);
1307         cache_loader=(CacheLoader)cl.newInstance();
1308         cache_loader.setConfig(cache_loader_config);
1309         cache_loader.setCache(this);
1310         cache_loader.create();
1311         cache_loader.start();
1312      }
1313   }
1314
1315   protected void cacheLoaderPreload() throws Exception JavaDoc {
1316      if(cache_loader != null) {
1317         if(log.isTraceEnabled())
1318            log.trace("preloading " + cache_loader_preload);
1319         if(cache_loader_preload != null) {
1320            for(Iterator it=cache_loader_preload.iterator(); it.hasNext();) {
1321               Fqn fqn=(Fqn)it.next();
1322               preload(fqn, true, true);
1323            }
1324         }
1325      }
1326   }
1327
1328
1329   /**
1330    * Loads the indicated Fqn, plus all parents recursively from the CacheLoader. If no CacheLoader is present,
1331    * this is a no-op
1332    * @param fqn
1333    * @throws Exception
1334    * @jmx.managed-operation
1335    */

1336   public void load(String JavaDoc fqn) throws Exception JavaDoc {
1337      if(cache_loader != null)
1338         preload(Fqn.fromString(fqn), true, true);
1339   }
1340
1341   void preload(Fqn fqn, boolean preload_parents, boolean preload_children) throws Exception JavaDoc {
1342
1343      // 1. Load the attributes first
1344
this.get(fqn, "bla");
1345
1346      // 2. Then load the parents
1347
if(preload_parents) {
1348         Fqn tmp_fqn=new Fqn();
1349         for(int i=0; i < fqn.size()-1; i++) {
1350            tmp_fqn=new Fqn(tmp_fqn, fqn.get(i));
1351            this.get(tmp_fqn, "bla");
1352         }
1353      }
1354
1355      if(preload_children == false)
1356         return;
1357
1358      // 3. Then recursively for all child nodes, preload them as well
1359
Set children=cache_loader.getChildrenNames(fqn);
1360      if(children == null)
1361         return;
1362      for(Iterator it=children.iterator(); it.hasNext();) {
1363         String JavaDoc child_name=(String JavaDoc)it.next();
1364         Fqn child_fqn=new Fqn(fqn, child_name);
1365         preload(child_fqn, false, true);
1366      }
1367   }
1368
1369   void destroyCacheLoader() {
1370      if(cache_loader != null) {
1371         cache_loader.stop();
1372         cache_loader.destroy();
1373         cache_loader=null;
1374      }
1375   }
1376
1377   protected boolean determineCoordinator() {
1378      if(channel == null)
1379         return false;
1380      Object JavaDoc local_addr=getLocalAddress();
1381      if(local_addr == null)
1382         return false;
1383      View view=channel.getView();
1384      if(view == null) return false;
1385      ViewId vid=view.getVid();
1386      if(vid == null) return false;
1387      Object JavaDoc coord=vid.getCoordAddress();
1388      if(coord == null) return false;
1389      return local_addr.equals(coord);
1390   }
1391
1392   public Address getCoordinator() {
1393      if(channel == null) return null;
1394      View view=channel.getView();
1395      if(view == null) return null;
1396      ViewId vid=view.getVid();
1397      if(vid == null) return null;
1398      Address coord=vid.getCoordAddress();
1399      return coord;
1400   }
1401
1402   public byte[] getStateBytes() {
1403      return this.getMessageListener().getState();
1404   }
1405
1406   public void setStateBytes(byte[] state) {
1407      this.getMessageListener().setState(state);
1408   }
1409
1410   protected void fetchStateOnStartup() throws Exception JavaDoc {
1411      long start, stop;
1412      synchronized(stateLock) {
1413         isStateSet=false;
1414         start=System.currentTimeMillis();
1415         boolean rc=channel.getState(null, state_fetch_timeout);
1416         if(rc) {
1417            while(!isStateSet) {
1418               try {
1419                  stateLock.wait();
1420               }
1421               catch(InterruptedException JavaDoc iex) {
1422               }
1423            }
1424            stop=System.currentTimeMillis();
1425            log.info("state was retrieved successfully (in " + (stop-start) + " milliseconds)");
1426         }
1427         else
1428            log.info("state could not be retrieved (must be first member in group)");
1429      }
1430   }
1431
1432   /**
1433    * @jmx.managed-operation
1434    */

1435   public void stopService() {
1436      if(channel != null) {
1437         log.info("stopService(): closing the channel");
1438         channel.close();
1439         channel=null;
1440      }
1441      if(disp != null) {
1442         log.info("stopService(): stopping the dispatcher");
1443         disp.stop();
1444         disp=null;
1445      }
1446      if(members != null && members.size() > 0)
1447         members.clear();
1448
1449      if(repl_queue != null)
1450         repl_queue.stop();
1451
1452      destroyCacheLoader();
1453
1454      notifyCacheStopped();
1455
1456      // Need to clean up listeners as well
1457
listeners.clear();
1458   }
1459
1460   /* ----------------------- End of MBeanSupport ----------------------- */
1461
1462   /**
1463    * @param fqn fqn String name to retrieve from cache
1464    * @return Node corresponding to the fqn. Null if does not exist. No guarantees wrt replication,
1465    * cache loading are given if the underlying node is modified
1466    */

1467   public Node get(String JavaDoc fqn) throws CacheException {
1468      return get(Fqn.fromString(fqn));
1469   }
1470
1471   /**
1472    * @param fqn fqn instance to retrieve from cache
1473    * @return Node corresponding to the fqn. Null if does not exist. No guarantees wrt replication,
1474    * cache loading are given if the underlying node is modified
1475    */

1476   public Node get(Fqn fqn) throws CacheException {
1477      MethodCall m=new MethodCall(getNodeMethodLocal, new Object JavaDoc[]{fqn});
1478      return (Node)invokeMethod(m);
1479   }
1480
1481   public Node _get(Fqn fqn) throws CacheException {
1482      return findNode(fqn);
1483   }
1484
1485
1486   /**
1487    * @param fqn
1488    * @return
1489    * @jmx.managed-operation
1490    */

1491   public Set getKeys(String JavaDoc fqn) throws CacheException {
1492      return getKeys(Fqn.fromString(fqn));
1493   }
1494
1495   /**
1496    * @param fqn
1497    * @return A Set<String> of keys. This is a copy of the key set, modifications will not be written to the original.
1498    * Returns null if the node is not found, or the node has no attributes
1499    * @jmx.managed-operation
1500    */

1501   public Set getKeys(Fqn fqn) throws CacheException {
1502      MethodCall m=new MethodCall(getKeysMethodLocal, new Object JavaDoc[]{fqn});
1503      return (Set)invokeMethod(m);
1504   }
1505
1506
1507   public Set _getKeys(Fqn fqn) throws CacheException {
1508      Set retval=null;
1509      Node n=findNode(fqn);
1510      if(n == null)
1511         return null;
1512      retval=n.getDataKeys();
1513      return retval != null? new LinkedHashSet(retval) : null;
1514   }
1515
1516   /**
1517    * Finds a node given its name and returns the value associated with a given key in its <code>data</code>
1518    * map. Returns null if the node was not found in the tree or the key was not found in the hashmap.
1519    *
1520    * @param fqn The fully qualified name of the node.
1521    * @param key The key.
1522    * @jmx.managed-operation
1523    */

1524   public Object JavaDoc get(String JavaDoc fqn, Object JavaDoc key) throws CacheException {
1525      return get(Fqn.fromString(fqn), key);
1526   }
1527
1528
1529   /**
1530    * Finds a node given its name and returns the value associated with a given key in its <code>data</code>
1531    * map. Returns null if the node was not found in the tree or the key was not found in the hashmap.
1532    *
1533    * @param fqn The fully qualified name of the node.
1534    * @param key The key.
1535    * @jmx.managed-operation
1536    */

1537   public Object JavaDoc get(Fqn fqn, Object JavaDoc key) throws CacheException {
1538      return get(fqn, key, true);
1539   }
1540
1541   public Object JavaDoc _get(Fqn fqn, Object JavaDoc key, boolean sendNodeEvent) throws CacheException {
1542      if(log.isTraceEnabled())
1543         log.trace("_get(" + ", \"" + fqn + "\", " + key + ", \"" +sendNodeEvent +"\")");
1544      Node n=findNode(fqn);
1545      if(n == null) return null;
1546      if(sendNodeEvent)
1547         notifyNodeVisisted(fqn);
1548      return n.get(key);
1549   }
1550
1551
1552   protected Object JavaDoc get(Fqn fqn, Object JavaDoc key, boolean sendNodeEvent) throws CacheException {
1553      MethodCall m=new MethodCall(getKeyValueMethodLocal, new Object JavaDoc[]{fqn, key, new Boolean JavaDoc(sendNodeEvent)});
1554      return invokeMethod(m);
1555   }
1556
1557   /**
1558    * Like <code>get()</code> method but without triggering a node visit event. This is used
1559    * to prevent refresh of the cache data in the eviction policy.
1560    * @param fqn
1561    * @param key
1562    * @return
1563    */

1564   public Object JavaDoc peek(Fqn fqn, Object JavaDoc key) throws CacheException {
1565      return get(fqn, key, false);
1566   }
1567
1568
1569
1570   /**
1571    * Checks whether a given node exists in the tree
1572    *
1573    * @param fqn The fully qualified name of the node
1574    * @return boolean Whether or not the node exists
1575    * @jmx.managed-operation
1576    */

1577   public boolean exists(String JavaDoc fqn) {
1578      return exists(Fqn.fromString(fqn));
1579   }
1580
1581
1582   /**
1583    * Checks whether a given node exists in the tree. Does not acquire any locks in doing so (result may be dirty read)
1584    * @param fqn The fully qualified name of the node
1585    * @return boolean Whether or not the node exists
1586    * @jmx.managed-operation
1587    */

1588   public boolean exists(Fqn fqn) {
1589      Node n=findInternal(fqn);
1590      return n != null;
1591   }
1592
1593   /**
1594    * Gets node without attempt to load it from CacheLoader if not present
1595    * @param fqn
1596    * @return
1597    */

1598   private Node findInternal(Fqn fqn) {
1599      if(fqn == null || fqn.size() == 0) return root;
1600      Node n=root, retval=null;
1601      Object JavaDoc obj;
1602      for(int i=0; i < fqn.size(); i++) {
1603         obj=fqn.get(i);
1604         n=n.getChild(obj);
1605         if(n == null)
1606            return null;
1607         else
1608            retval=n;
1609      }
1610      return retval;
1611   }
1612
1613
1614   /**
1615    *
1616    * @param fqn
1617    * @param key
1618    * @return
1619    * @jmx.managed-operation
1620    */

1621   public boolean exists(String JavaDoc fqn, Object JavaDoc key) {
1622      return exists(Fqn.fromString(fqn), key);
1623   }
1624
1625
1626   /**
1627    * Checks whether a given key exists in the given node. Does not interact with CacheLoader, so the behavior is
1628    * different from {@link #get(Fqn,Object)}
1629    * @param fqn The fully qualified name of the node
1630    * @param key
1631    * @return boolean Whether or not the node exists
1632    * @jmx.managed-operation
1633    */

1634   public boolean exists(Fqn fqn, Object JavaDoc key) {
1635      Node n=findInternal(fqn);
1636      if(n == null)
1637         return false;
1638      else
1639         return n.containsKey(key);
1640    }
1641
1642
1643   /**
1644    * Adds a new node to the tree and sets its data. If the node doesn not yet exist, it will be created.
1645    * Also, parent nodes will be created if not existent. If the node already has data, then the new data
1646    * will override the old one. If the node already existed, a nodeModified() notification will be generated.
1647    * Otherwise a nodeCreated() motification will be emitted.
1648    *
1649    * @param fqn The fully qualified name of the new node
1650    * @param data The new data. May be null if no data should be set in the node.
1651    * @jmx.managed-operation
1652    */

1653   public void put(String JavaDoc fqn, Map JavaDoc data) throws CacheException {
1654      put(Fqn.fromString(fqn), data);
1655   }
1656
1657   /**
1658    * Adds a new node to the tree and sets its data. If the node doesn not yet exist, it will be created.
1659    * Also, parent nodes will be created if not existent. If the node already has data, then the new data
1660    * will override the old one. If the node already existed, a nodeModified() notification will be generated.
1661    * Otherwise a nodeCreated() motification will be emitted.
1662    *
1663    * @param fqn The fully qualified name of the new node
1664    * @param data The new data. May be null if no data should be set in the node.
1665    * @jmx.managed-operation
1666    */

1667   public void put(Fqn fqn, Map JavaDoc data) throws CacheException {
1668      GlobalTransaction tx=getCurrentTransaction();
1669      MethodCall m=new MethodCall(putDataMethodLocal, new Object JavaDoc[]{tx, fqn, data, Boolean.TRUE});
1670      invokeMethod(m);
1671   }
1672
1673   /**
1674    * Adds a key and value to a given node. If the node doesn't exist, it will be created. If the node
1675    * already existed, a nodeModified() notification will be generated. Otherwise a
1676    * nodeCreated() motification will be emitted.
1677    *
1678    * @param fqn The fully qualified name of the node
1679    * @param key The key
1680    * @param value The value
1681    * @return Object The previous value (if any), if node was present
1682    * @jmx.managed-operation
1683    */

1684   public Object JavaDoc put(String JavaDoc fqn, Object JavaDoc key, Object JavaDoc value) throws CacheException {
1685      return put(Fqn.fromString(fqn), key, value);
1686   }
1687
1688
1689   /**
1690    * Put with the following properties:
1691    * <ol>
1692    * <li>Fails fast (after timeout milliseconds)
1693    * <li>If replication is used: replicates <em>asynchronously</em>, overriding a potential synchronous mode
1694    * </ol>
1695    * This method should be used without running in a transaction (suspend()/resume() before calling it)
1696    * @param fqn The fully qualified name of the node
1697    * @param key
1698    * @param value
1699    * @param timeout Number of milliseconds to wait until a lock has been acquired. A TimeoutException will
1700    * be thrown if not successful. 0 means to wait forever
1701    * @return
1702    * @throws CacheException
1703    * @deprecated This is a kludge created specifically form the Hibernate 3.0 release. This method should
1704    * <em>not</em> be used by any application. The methodV will likely be removed in a future release
1705    */

1706   public Object JavaDoc putFailFast(Fqn fqn, Object JavaDoc key, Object JavaDoc value, long timeout) throws CacheException {
1707      GlobalTransaction tx=getCurrentTransaction();
1708      MethodCall m=new MethodCall(putFailFastKeyValueMethodLocal,
1709                                  new Object JavaDoc[]{tx, fqn, key, value, Boolean.TRUE, new Long JavaDoc(timeout)});
1710      return invokeMethod(m);
1711   }
1712
1713   /**
1714    *
1715    * @param fqn
1716    * @param key
1717    * @param value
1718    * @param timeout
1719    * @return
1720    * @throws CacheException
1721    * @deprecated
1722    */

1723   public Object JavaDoc putFailFast(String JavaDoc fqn, Object JavaDoc key, Object JavaDoc value, long timeout) throws CacheException {
1724      GlobalTransaction tx=getCurrentTransaction();
1725      Fqn fqntmp=Fqn.fromString(fqn);
1726      MethodCall m=new MethodCall(putFailFastKeyValueMethodLocal,
1727                                  new Object JavaDoc[]{tx, fqntmp, key, value, Boolean.TRUE, new Long JavaDoc(timeout)});
1728      return invokeMethod(m);
1729   }
1730
1731   /**
1732    * Adds a key and value to a given node. If the node doesn't exist, it will be created. If the node
1733    * already existed, a nodeModified() notification will be generated. Otherwise a
1734    * nodeCreated() motification will be emitted.
1735    *
1736    * @param fqn The fully qualified name of the node
1737    * @param key The key
1738    * @param value The value
1739    * @return Object The previous value (if any), if node was present
1740    * @jmx.managed-operation
1741    */

1742   public Object JavaDoc put(Fqn fqn, Object JavaDoc key, Object JavaDoc value) throws CacheException {
1743      GlobalTransaction tx=getCurrentTransaction();
1744      MethodCall m=new MethodCall(putKeyValMethodLocal, new Object JavaDoc[]{tx, fqn, key, value, Boolean.TRUE});
1745      return invokeMethod(m);
1746   }
1747
1748   /**
1749    * Removes the node from the tree.
1750    *
1751    * @param fqn The fully qualified name of the node.
1752    * @jmx.managed-operation
1753    */

1754   public void remove(String JavaDoc fqn) throws CacheException {
1755      remove(Fqn.fromString(fqn));
1756   }
1757
1758   /**
1759    * Removes the node from the tree.
1760    *
1761    * @param fqn The fully qualified name of the node.
1762    * @jmx.managed-operation
1763    */

1764   public void remove(Fqn fqn) throws CacheException {
1765      GlobalTransaction tx=getCurrentTransaction();
1766      MethodCall m=new MethodCall(removeNodeMethodLocal, new Object JavaDoc[]{tx, fqn, Boolean.TRUE});
1767      invokeMethod(m);
1768   }
1769
1770   /**
1771    * Called by eviction policy provider. Note that eviction is done only in local mode,
1772    * that is, it doesn't replicate the node removal. This is will cause the replcation nodes
1773    * not synchronizing, but it is ok since user is supposed to add the node again when get is
1774    * null. After that, the contents will be in sync.
1775    * @param fqn Will remove everythign assoicated with this fqn.
1776    * @throws CacheException
1777    * @jmx.managed-operation
1778    */

1779   public void evict(Fqn fqn) throws CacheException {
1780      MethodCall m=new MethodCall(evictNodeMethodLocal, new Object JavaDoc[]{fqn});
1781      invokeMethod(m);
1782   }
1783
1784
1785   /**
1786    * Evicts a key/value pair from a node's attributes. Note that this is <em>local</em>, will not be replicated.
1787    * @param fqn
1788    * @param key
1789    * @throws CacheException
1790    * @jmx.managed-operation
1791    */

1792// public void evict(Fqn fqn, Object key) throws CacheException {
1793
// MethodCall m=new MethodCall(evictKeyValueMethodLocal, new Object[]{fqn, key});
1794
// invokeMethod(m);
1795
// }
1796

1797
1798
1799
1800   /**
1801    * Removes <code>key</code> from the node's hashmap
1802    *
1803    * @param fqn The fullly qualified name of the node
1804    * @param key The key to be removed
1805    * @return The previous value, or null if none was associated with the given key
1806    * @jmx.managed-operation
1807    */

1808   public Object JavaDoc remove(String JavaDoc fqn, Object JavaDoc key) throws CacheException {
1809      return remove(Fqn.fromString(fqn), key);
1810   }
1811
1812   /**
1813    * Removes <code>key</code> from the node's hashmap
1814    *
1815    * @param fqn The fullly qualified name of the node
1816    * @param key The key to be removed
1817    * @return The previous value, or null if none was associated with the given key
1818    * @jmx.managed-operation
1819    */

1820   public Object JavaDoc remove(Fqn fqn, Object JavaDoc key) throws CacheException {
1821      GlobalTransaction tx=getCurrentTransaction();
1822      MethodCall m=new MethodCall(removeKeyMethodLocal, new Object JavaDoc[]{tx, fqn, key, Boolean.TRUE});
1823      return invokeMethod(m);
1824   }
1825
1826   /**
1827    *
1828    * @param fqn
1829    * @throws CacheException
1830    * @jmx.managed-operation
1831    */

1832   public void removeData(String JavaDoc fqn) throws CacheException {
1833      removeData(Fqn.fromString(fqn));
1834   }
1835
1836   /**
1837    *
1838    * @param fqn
1839    * @throws CacheException
1840    * @jmx.managed-operation
1841    */

1842   public void removeData(Fqn fqn) throws CacheException {
1843      GlobalTransaction tx=getCurrentTransaction();
1844      MethodCall m=new MethodCall(removeDataMethodLocal, new Object JavaDoc[]{tx, fqn, Boolean.TRUE});
1845      invokeMethod(m);
1846   }
1847
1848
1849   /**
1850    * Lock a given node (or the entire subtree starting at this node)
1851    * @param fqn The FQN of the node
1852    * @param owner The owner. This is simply a key into a hashtable, and can be anything, e.g.
1853    * a GlobalTransaction, the current thread, or a special object. If null, it is set to Thread.currentThread()
1854    * @param lock_type The type of lock (RO, RW). Needs to be of type Node.LOCK_TYPE_READ or Node.LOCK_TYPE_WRITE
1855    * @param lock_recursive If true, the entire subtree is locked, else only the given node
1856    * @throws CacheException If node doesn't exist, a NodeNotExistsException is throw. Other exceptions are
1857    * LockingException, TimeoutException and UpgradeException
1858    */

1859// public void lock(Fqn fqn, Object owner, int lock_type, boolean lock_recursive) throws CacheException {
1860
//
1861
// }
1862

1863   /**
1864    * Unlock a given node (or the entire subtree starting at this node)
1865    * @param fqn The FQN of the node
1866    * @param owner The owner. This is simply a key into a hashtable, and can be anything, e.g.
1867    * a GlobalTransaction, the current thread, or a special object. If null, it is set to Thread.currentThread()
1868    * @param unlock_recursive If true, the entire subtree is unlocked, else only the given node
1869    * @param force Release the lock even if we're not the owner
1870    */

1871// public void unlock(Fqn fqn, Object owner, boolean unlock_recursive, boolean force) {
1872
//
1873
// }
1874

1875   /**
1876    * Force-releases all locks in this node and the entire subtree
1877    *
1878    * @param fqn
1879    * @jmx.managed-operation
1880    */

1881   public void releaseAllLocks(String JavaDoc fqn) {
1882      releaseAllLocks(Fqn.fromString(fqn));
1883   }
1884
1885   /**
1886    * Force-releases all locks in this node and the entire subtree
1887    *
1888    * @param fqn
1889    * @jmx.managed-operation
1890    */

1891   public void releaseAllLocks(Fqn fqn) {
1892      MethodCall m=new MethodCall(releaseAllLocksMethodLocal, new Object JavaDoc[]{fqn});
1893      try {
1894         invokeMethod(m);
1895      }
1896      catch(CacheException e) {
1897         log.error("failed releasing all locks for " + fqn, e);
1898      }
1899   }
1900
1901   /**
1902    * Prints a representation of the node defined by <code>fqn</code>.
1903    * Output includes name, fqn and data.
1904    *
1905    * @jmx.managed-operation
1906    */

1907   public String JavaDoc print(String JavaDoc fqn) {
1908      return print(Fqn.fromString(fqn));
1909   }
1910
1911   /**
1912    * Prints a representation of the node defined by <code>fqn</code>.
1913    * Output includes name, fqn and data.
1914    *
1915    * @jmx.managed-operation
1916    */

1917   public String JavaDoc print(Fqn fqn) {
1918      MethodCall m=new MethodCall(printMethodLocal, new Object JavaDoc[]{fqn});
1919      Object JavaDoc retval=null;
1920      try {
1921         retval=invokeMethod(m);
1922      }
1923      catch(Throwable JavaDoc e) {
1924         retval=e;
1925      }
1926      if(retval != null)
1927         return retval.toString();
1928      else return "";
1929   }
1930
1931
1932   /**
1933    * Returns all children of a given node
1934    *
1935    * @param fqn The fully qualified name of the node
1936    * @return Set A list of child names (as Strings)
1937    * @jmx.managed-operation
1938    */

1939   public Set getChildrenNames(String JavaDoc fqn) throws CacheException {
1940      return getChildrenNames(Fqn.fromString(fqn));
1941   }
1942
1943   /**
1944    * Returns all children of a given node
1945    *
1946    * @param fqn The fully qualified name of the node
1947    * @return Set A list of child names (as Objects). Must <em>not</em> be modified because this would
1948    * modify the underlying node directly (will throw an exception if modification is attempted). Returns null
1949    * of the parent node was not found, or if there are no children
1950    * @jmx.managed-operation
1951    */

1952   public Set getChildrenNames(Fqn fqn) throws CacheException {
1953      MethodCall m=new MethodCall(getChildrenNamesMethodLocal, new Object JavaDoc[]{fqn});
1954      return (Set)invokeMethod(m);
1955   }
1956
1957   public Set _getChildrenNames(Fqn fqn) throws CacheException {
1958      Node n=findNode(fqn);
1959      if(n == null) return null;
1960      Map JavaDoc m=n.getChildren();
1961      if(m != null)
1962         return new LinkedHashSet(m.keySet());
1963      else
1964         return null;
1965   }
1966
1967
1968
1969   public boolean hasChild(Fqn fqn) {
1970      if(fqn == null) return false;
1971
1972      Node n=root;
1973      Object JavaDoc obj;
1974      for(int i=0; i < fqn.size(); i++) {
1975         obj=fqn.get(i);
1976         n=n.getChild(obj);
1977         if(n == null)
1978            return false;
1979      }
1980      return n.hasChildren();
1981   }
1982
1983   /**
1984    * @return
1985    * @jmx.managed-operation
1986    */

1987   public String JavaDoc toString() {
1988      return toString(false);
1989   }
1990
1991
1992   /**
1993    * @return
1994    * @jmx.managed-operation
1995    */

1996   public String JavaDoc toString(boolean details) {
1997      StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
1998      int indent=0;
1999      Map JavaDoc children;
2000
2001      if(!details) {
2002         sb.append(getClass().getName()).append(" [").append(getNumberOfNodes()).append(" nodes, ");
2003         sb.append(getNumberOfLocksHeld()).append(" locks]");
2004      }
2005      else {
2006         children=root.getChildren();
2007         if(children != null && children.size() > 0) {
2008            Collection nodes=children.values();
2009            for(Iterator it=nodes.iterator(); it.hasNext();) {
2010               ((Node)it.next()).print(sb, indent);
2011               sb.append("\n");
2012            }
2013         }
2014         else
2015            sb.append(SEPARATOR);
2016      }
2017      return sb.toString();
2018   }
2019
2020
2021
2022
2023   /**
2024    * @return
2025    * @jmx.managed-operation
2026    */

2027   public String JavaDoc printDetails() {
2028      StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
2029      int indent=0;
2030      Map JavaDoc children;
2031
2032      children=root.getChildren();
2033      if(children != null && children.size() > 0) {
2034         Collection nodes=children.values();
2035         for(Iterator it=nodes.iterator(); it.hasNext();) {
2036            ((Node)it.next()).printDetails(sb, indent);
2037            sb.append("\n");
2038         }
2039      }
2040      else
2041         sb.append(SEPARATOR);
2042      return sb.toString();
2043   }
2044
2045   /**
2046    * @return
2047    * @jmx.managed-operation
2048    */

2049   public String JavaDoc printLockInfo() {
2050      StringBuffer JavaDoc sb=new StringBuffer JavaDoc("\n");
2051      int indent=0;
2052      Map JavaDoc children;
2053
2054      children=root.getChildren();
2055      if(children != null && children.size() > 0) {
2056         Collection nodes=children.values();
2057         for(Iterator it=nodes.iterator(); it.hasNext();) {
2058            ((Node)it.next()).printLockInfo(sb, indent);
2059            sb.append("\n");
2060         }
2061      }
2062      else
2063         sb.append(SEPARATOR);
2064      return sb.toString();
2065   }
2066
2067   /**
2068    * Gets the number of read or write locks held across the entire tree
2069    * @return
2070    * @jmx.managed-attribute
2071    */

2072   public int getNumberOfLocksHeld() {
2073      return numLocks(root);
2074   }
2075
2076
2077   int numLocks(Node n) {
2078      int num=0;
2079      Map JavaDoc children;
2080      if(n.isLocked())
2081         num++;
2082      if((children=n.getChildren()) != null) {
2083         for(Iterator it=children.values().iterator(); it.hasNext();) {
2084            num+=numLocks((Node)it.next());
2085         }
2086      }
2087      return num;
2088   }
2089
2090   /**
2091    * Returns an <em>approximation</em> of the total number of nodes in the tree. Since this method doesn't acquire
2092    * any locks, the number might be incorrect, or the method might even throw a ConcurrentModificationException
2093    * @return
2094    * @jmx.managed-attribute
2095    */

2096   public int getNumberOfNodes() {
2097      return numNodes(root)-1;
2098   }
2099
2100
2101   int numNodes(Node n) {
2102      if(n == null)
2103         return 0;
2104      int count=1; // for n
2105
if(n.hasChildren()) {
2106         Map JavaDoc children=n.getChildren();
2107         if(children != null && children.size() > 0) {
2108            Collection child_nodes=children.values();
2109            Node child;
2110            for(Iterator it=child_nodes.iterator(); it.hasNext();) {
2111               child=(Node)it.next();
2112               count+=numNodes(child);
2113            }
2114         }
2115      }
2116      return count;
2117   }
2118
2119   /**
2120    * Returns an <em>approximation</em> of the total number of attributes in the tree. Since this method doesn't acquire
2121    * any locks, the number might be incorrect, or the method might even throw a ConcurrentModificationException
2122    * @return
2123    * @jmx.managed-attribute
2124    */

2125   public int getNumberOfAttributes() {
2126      return numAttributes(root);
2127   }
2128
2129   int numAttributes(Node n) {
2130      if(n == null)
2131         return 0;
2132      int count=n.numAttributes();
2133      if(n.hasChildren()) {
2134         Map JavaDoc children=n.getChildren();
2135         if(children != null && children.size() > 0) {
2136            Collection child_nodes=children.values();
2137            Node child;
2138            for(Iterator it=child_nodes.iterator(); it.hasNext();) {
2139               child=(Node)it.next();
2140               count+=child.numAttributes();
2141            }
2142         }
2143      }
2144      return count;
2145   }
2146
2147
2148   /* ---------------------- Remote method calls -------------------- */
2149
2150   public List callRemoteMethods(Vector mbrs, MethodCall method_call,
2151                                 boolean synchronous, boolean exclude_self, long timeout)
2152           throws Exception JavaDoc {
2153      RspList rsps;
2154      Rsp rsp;
2155      List retval;
2156      Vector validMembers;
2157      int mode=synchronous ? GroupRequest.GET_ALL : GroupRequest.GET_NONE;
2158
2159      if(disp == null)
2160         return null;
2161
2162      validMembers=mbrs != null ? new Vector(mbrs) : new Vector(this.members);
2163      if(exclude_self && validMembers.size() > 0) {
2164         Object JavaDoc local_addr=getLocalAddress();
2165         if(local_addr != null)
2166            validMembers.remove(local_addr);
2167      }
2168      if(validMembers.size() == 0) {
2169         if(log.isTraceEnabled())
2170            log.trace("destination list is empty, discarding call");
2171         return null;
2172      }
2173
2174      if(log.isTraceEnabled())
2175         log.trace("callRemoteMethods(): valid members are " + validMembers);
2176
2177      rsps=disp.callRemoteMethods(validMembers, method_call, mode, timeout);
2178      if(log.isTraceEnabled())
2179         log.trace("(" + getLocalAddress() + "): responses for method " + method_call.getName() + ":\n" + rsps);
2180      if(rsps == null)
2181         return null;
2182      retval=new ArrayList(rsps.size());
2183      for(int i=0; i < rsps.size(); i++) {
2184         rsp=(Rsp)rsps.elementAt(i);
2185         if(rsp.wasSuspected() || !rsp.wasReceived())
2186            retval.add(new TimeoutException("rsp=" + rsp));
2187         else
2188            retval.add(rsp.getValue());
2189      }
2190      return retval;
2191   }
2192
2193
2194   /**
2195    * @param members
2196    * @param method
2197    * @param args
2198    * @param synchronous
2199    * @param exclude_self
2200    * @param timeout
2201    * @return
2202    * @throws Exception
2203    * @jmx.managed-operation
2204    */

2205   public List callRemoteMethods(Vector members, Method JavaDoc method, Object JavaDoc[] args,
2206                                 boolean synchronous, boolean exclude_self, long timeout)
2207           throws Exception JavaDoc {
2208      return callRemoteMethods(members, new MethodCall(method, args), synchronous, exclude_self, timeout);
2209   }
2210
2211   /**
2212    * @param members
2213    * @param method_name
2214    * @param types
2215    * @param args
2216    * @param synchronous
2217    * @param exclude_self
2218    * @param timeout
2219    * @return
2220    * @throws Exception
2221    * @jmx.managed-operation
2222    */

2223   public List callRemoteMethods(Vector members, String JavaDoc method_name,
2224                                 Class JavaDoc[] types, Object JavaDoc[] args,
2225                                 boolean synchronous, boolean exclude_self, long timeout)
2226           throws Exception JavaDoc {
2227      Method JavaDoc method=getClass().getDeclaredMethod(method_name, types);
2228      return callRemoteMethods(members, method, args, synchronous, exclude_self, timeout);
2229   }
2230   /* -------------------- End Remote method calls ------------------ */
2231
2232
2233   /* --------------------- Callbacks -------------------------- */
2234
2235   /**
2236    * Does the real work. Needs to acquire locks if accessing nodes, depending on
2237    * the value of <tt>locking</tt>. If run inside a transaction, needs to (a) add
2238    * newly acquired locks to {@link TransactionEntry}'s lock list, (b) add nodes
2239    * that were created to {@link TransactionEntry}'s node list and (c) create
2240    * {@link Modification}s and add them to {@link TransactionEntry}'s modification
2241    * list and (d) create compensating modifications to undo the changes in case
2242    * of a rollback
2243    *
2244    * @param fqn
2245    * @param data
2246    * @param create_undo_ops If true, undo operations will be created (default is true).
2247    * Otherwise they will not be created (used by rollback()).
2248    * @jmx.managed-operation
2249    */

2250   public void _put(GlobalTransaction tx, String JavaDoc fqn, Map JavaDoc data, boolean create_undo_ops)
2251           throws CacheException {
2252      _put(tx, Fqn.fromString(fqn), data, create_undo_ops);
2253   }
2254
2255
2256   /**
2257    * Does the real work. Needs to acquire locks if accessing nodes, depending on
2258    * the value of <tt>locking</tt>. If run inside a transaction, needs to (a) add
2259    * newly acquired locks to {@link TransactionEntry}'s lock list, (b) add nodes
2260    * that were created to {@link TransactionEntry}'s node list and (c) create
2261    * {@link Modification}s and add them to {@link TransactionEntry}'s modification
2262    * list and (d) create compensating modifications to undo the changes in case
2263    * of a rollback
2264    *
2265    * @param fqn
2266    * @param data
2267    * @param create_undo_ops If true, undo operations will be created (default is true).
2268    * Otherwise they will not be created (used by rollback()).
2269    * @jmx.managed-operation
2270    */

2271   public void _put(GlobalTransaction tx, Fqn fqn, Map JavaDoc data, boolean create_undo_ops)
2272           throws CacheException {
2273      _put(tx, fqn, data, create_undo_ops, false);
2274   }
2275
2276
2277   /**
2278    * Does the real work. Needs to acquire locks if accessing nodes, depending on
2279    * the value of <tt>locking</tt>. If run inside a transaction, needs to (a) add
2280    * newly acquired locks to {@link TransactionEntry}'s lock list, (b) add nodes
2281    * that were created to {@link TransactionEntry}'s node list and (c) create
2282    * {@link Modification}s and add them to {@link TransactionEntry}'s modification
2283    * list and (d) create compensating modifications to undo the changes in case
2284    * of a rollback
2285    *
2286    * @param fqn
2287    * @param data
2288    * @param create_undo_ops If true, undo operations will be created (default is true).
2289    * @param erase_contents Clear the existing hashmap before putting the new data into it
2290    * Otherwise they will not be created (used by rollback()).
2291    * @jmx.managed-operation
2292    */

2293   public void _put(GlobalTransaction tx, Fqn fqn, Map JavaDoc data, boolean create_undo_ops, boolean erase_contents)
2294           throws CacheException {
2295      Node n;
2296      MethodCall undo_op=null;
2297      Map JavaDoc old_data;
2298
2299      if(log.isTraceEnabled())
2300         log.trace(new StringBuffer JavaDoc().append("_put(").append(tx).append(", \"").append(fqn)
2301                   .append("\", ").append(data).append(")").toString());
2302
2303      // Find the node. This will lock it (if <tt>locking</tt> is true) and
2304
// add the temporarily created parent nodes to the TX's node list if tx != null)
2305
n=findNode(fqn);
2306      if(n == null) {
2307         String JavaDoc errStr="node " + fqn + " not found (gtx=" + tx + ", caller=" + Thread.currentThread() + ")";
2308         if(log.isTraceEnabled())
2309            log.trace(errStr);
2310         throw new NodeNotExistsException(errStr);
2311      }
2312
2313      // TODO: move creation of undo-operations to separate Interceptor
2314
// create a compensating method call (reverting the effect of
2315
// this modification) and put it into the TX's undo list.
2316
if(tx != null && create_undo_ops) {
2317         // TODO even if n is brand new, getData can have empty value instead. Need to fix.
2318
if((old_data=n.getData()) == null) {
2319            undo_op=new MethodCall(removeDataMethodLocal,
2320                                   new Object JavaDoc[]{tx, fqn, Boolean.FALSE});
2321         }
2322         else {
2323            undo_op=new MethodCall(putDataEraseMethodLocal,
2324                                   new Object JavaDoc[]{tx, fqn,
2325                                                new HashMap(old_data),
2326                                                Boolean.FALSE,
2327                                                Boolean.TRUE}); // erase previous hashmap contents
2328
}
2329      }
2330
2331      n.put(data, erase_contents);
2332
2333      if(tx != null && create_undo_ops) {
2334         // 1. put undo-op in TX' undo-operations list (needed to rollback TX)
2335
tx_table.addUndoOperation(tx, undo_op);
2336      }
2337      notifyNodeModified(fqn);
2338   }
2339
2340
2341   /**
2342    * @param fqn
2343    * @param key
2344    * @param value
2345    * @return Previous value (if any)
2346    * @jmx.managed-operation
2347    */

2348   public Object JavaDoc _put(GlobalTransaction tx, String JavaDoc fqn, Object JavaDoc key, Object JavaDoc value, boolean create_undo_ops)
2349         throws CacheException {
2350      return _put(tx, Fqn.fromString(fqn), key, value, create_undo_ops);
2351   }
2352
2353
2354
2355
2356   /**
2357    * @param fqn
2358    * @param key
2359    * @param value
2360    * @return Previous value (if any)
2361    * @jmx.managed-operation
2362    */

2363   public Object JavaDoc _put(GlobalTransaction tx, Fqn fqn, Object JavaDoc key, Object JavaDoc value, boolean create_undo_ops)
2364         throws CacheException {
2365      Node n=null;
2366      MethodCall undo_op=null;
2367      Object JavaDoc old_value=null;
2368
2369      if(log.isTraceEnabled()) {
2370         log.trace(new StringBuffer JavaDoc().append("_put(").append(tx).append(", \"").
2371                   append(fqn).append("\", ").append(key).append(", ").append(value).append(")").toString());
2372      }
2373
2374      n=findNode(fqn);
2375      if(n == null) {
2376         String JavaDoc errStr="node " + fqn + " not found (gtx=" + tx + ", caller=" + Thread.currentThread() + ")";
2377         if(log.isTraceEnabled())
2378            log.trace(errStr);
2379         throw new NodeNotExistsException(errStr);
2380      }
2381
2382      old_value=n.get(key);
2383      n.put(key, value);
2384
2385      // create a compensating method call (reverting the effect of
2386
// this modification) and put it into the TX's undo list.
2387
if(tx != null && create_undo_ops) {
2388         if(old_value == null) {
2389            undo_op=new MethodCall(removeKeyMethodLocal,
2390                                   new Object JavaDoc[]{tx, fqn, key, Boolean.FALSE});
2391         }
2392         else {
2393            undo_op=new MethodCall(putKeyValMethodLocal,
2394                                   new Object JavaDoc[]{tx, fqn, key, old_value,
2395                                                Boolean.FALSE});
2396         }
2397         // 1. put undo-op in TX' undo-operations list (needed to rollback TX)
2398
tx_table.addUndoOperation(tx, undo_op);
2399      }
2400
2401      notifyNodeModified(fqn);
2402      return old_value;
2403   }
2404
2405
2406   public Object JavaDoc _put(GlobalTransaction tx, Fqn fqn, Object JavaDoc key, Object JavaDoc value, boolean create_undo_ops, long timeout)
2407         throws CacheException {
2408      return _put(tx, fqn, key, value, create_undo_ops);
2409   }
2410
2411   /**
2412    * @param fqn
2413    * @jmx.managed-operation
2414    */

2415   public void _remove(GlobalTransaction tx, String JavaDoc fqn, boolean create_undo_ops) throws CacheException {
2416      _remove(tx, Fqn.fromString(fqn), create_undo_ops);
2417   }
2418
2419   /**
2420    * @param fqn
2421    * @jmx.managed-operation
2422    */

2423   public void _remove(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops) throws CacheException {
2424      _remove(tx, fqn, create_undo_ops, true);
2425   }
2426
2427   public void _remove(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops, boolean sendNodeEvent)
2428           throws CacheException {
2429      _remove(tx, fqn, create_undo_ops, sendNodeEvent, false);
2430   }
2431
2432   /**
2433    * Remove a node
2434    * @param tx
2435    * @param fqn
2436    * @param create_undo_ops
2437    * @param sendNodeEvent
2438    */

2439   public void _remove(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops, boolean sendNodeEvent, boolean eviction)
2440         throws CacheException {
2441      Node n, parent_node;
2442      MethodCall undo_op=null;
2443
2444      if(log.isTraceEnabled())
2445         log.trace("_remove(" + tx + ", \"" + fqn + "\")");
2446
2447      if(fqn.size() == 0) {
2448         Set children=getChildrenNames(fqn);
2449         if(children != null) {
2450            Object JavaDoc[] kids=children.toArray();
2451
2452            for(int i=0; i < kids.length; i++) {
2453               Object JavaDoc s=kids[i];
2454               Fqn tmp=new Fqn(fqn, s);
2455               try {
2456                  _remove(tx, tmp, create_undo_ops, true, eviction);
2457               }
2458               catch(Exception JavaDoc e) {
2459                  log.error("failure removing node " + tmp);
2460               }
2461            }
2462         }
2463         return;
2464      }
2465
2466      // Find the node. This will add the temporarily created parent nodes to the TX's node list if tx != null)
2467
n=findNode(fqn);
2468      if(n == null) {
2469         log.warn("node " + fqn + " not found");
2470         return;
2471      }
2472      parent_node=n.getParent();
2473
2474      // remove subtree from parent
2475
parent_node.removeChild(n.getName());
2476
2477      // release all locks for the entire subtree
2478
n.releaseAll(tx != null? tx : (Object JavaDoc)Thread.currentThread());
2479
2480      // create a compensating method call (reverting the effect of
2481
// this modification) and put it into the TX's undo list.
2482
if(tx != null && create_undo_ops && n != null && eviction == false) {
2483         undo_op=new MethodCall(addChildMethodLocal, new Object JavaDoc[]{tx, parent_node.getFqn(), n.getName(), n});
2484
2485         // 1. put undo-op in TX' undo-operations list (needed to rollback TX)
2486
tx_table.addUndoOperation(tx, undo_op);
2487      }
2488
2489      if(sendNodeEvent)
2490         notifyNodeRemoved(fqn);
2491      else
2492         notifyNodeEvicted(fqn);
2493   }
2494
2495   /**
2496    * @param fqn
2497    * @param key
2498    * @return Object
2499    * @jmx.managed-operation
2500    */

2501   public Object JavaDoc _remove(GlobalTransaction tx, String JavaDoc fqn, Object JavaDoc key, boolean create_undo_ops)
2502           throws CacheException {
2503      return _remove(tx, Fqn.fromString(fqn), key, create_undo_ops);
2504   }
2505
2506   /**
2507    * @param fqn
2508    * @param key
2509    * @return Object
2510    * @jmx.managed-operation
2511    */

2512   public Object JavaDoc _remove(GlobalTransaction tx, Fqn fqn, Object JavaDoc key, boolean create_undo_ops)
2513           throws CacheException {
2514      return _remove(tx, fqn, key, create_undo_ops, false); // din't node remove notification event
2515
}
2516
2517   public Object JavaDoc _remove(GlobalTransaction tx, Fqn fqn, Object JavaDoc key, boolean create_undo_ops, boolean sendNodeEvent)
2518           throws CacheException {
2519      Node n=null;
2520      MethodCall undo_op=null;
2521      Object JavaDoc old_value=null;
2522
2523      if(log.isTraceEnabled())
2524         log.trace(new StringBuffer JavaDoc().append("_remove(").append(tx).append(", \"")
2525                   .append(fqn).append("\", ").append(key).append(")").toString());
2526
2527      // Find the node. This will lock it (if <tt>locking</tt> is true) and
2528
// add the temporarily created parent nodes to the TX's node list if tx != null)
2529
n=findNode(fqn);
2530      if(n == null) {
2531         log.warn("node " + fqn + " not found");
2532         return null;
2533      }
2534      old_value=n.remove(key);
2535
2536      // create a compensating method call (reverting the effect of
2537
// this modification) and put it into the TX's undo list.
2538
if(tx != null && create_undo_ops && old_value != null) {
2539         undo_op=new MethodCall(putKeyValMethodLocal,
2540                                new Object JavaDoc[]{tx, fqn, key, old_value,
2541                                             Boolean.FALSE});
2542         // 1. put undo-op in TX' undo-operations list (needed to rollback TX)
2543
tx_table.addUndoOperation(tx, undo_op);
2544      }
2545
2546      if(sendNodeEvent)
2547         notifyNodeModified(fqn); // changed from notifyNodeRemoved() - Jimmy Wilson
2548
return old_value;
2549   }
2550
2551   /**
2552    * @param fqn
2553    */

2554   public void _removeData(GlobalTransaction tx, String JavaDoc fqn, boolean create_undo_ops)
2555           throws CacheException {
2556      _removeData(tx, Fqn.fromString(fqn), create_undo_ops);
2557   }
2558
2559   /**
2560    * @param fqn
2561    */

2562   public void _removeData(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops)
2563           throws CacheException {
2564      _removeData(tx, fqn, create_undo_ops, true);
2565   }
2566
2567
2568   public void _removeData(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops, boolean sendNodeEvent)
2569           throws CacheException {
2570      _removeData(tx, fqn, create_undo_ops, sendNodeEvent, false);
2571   }
2572
2573   public void _removeData(GlobalTransaction tx, Fqn fqn, boolean create_undo_ops, boolean sendNodeEvent, boolean eviction)
2574           throws CacheException {
2575      Node n=null;
2576      MethodCall undo_op=null;
2577      Map JavaDoc old_data=null;
2578
2579      if(log.isTraceEnabled())
2580         log.trace("_removeData(" + tx + ", \"" + fqn + "\")");
2581
2582      // Find the node. This will lock it (if <tt>locking</tt> is true) and
2583
// add the temporarily created parent nodes to the TX's node list if tx != null)
2584
n=findNode(fqn);
2585      if(n == null) {
2586         log.warn("node " + fqn + " not found");
2587         return;
2588      }
2589
2590      // create a compensating method call (reverting the effect of
2591
// this modification) and put it into the TX's undo list.
2592
if(tx != null && create_undo_ops && (old_data=n.getData()) != null && !eviction) {
2593         undo_op=new MethodCall(putDataMethodLocal, new Object JavaDoc[]{tx, fqn, new HashMap(old_data), Boolean.FALSE});
2594      }
2595
2596      n.clear();
2597      if(eviction)
2598         n.put(UNINITIALIZED, null); // required by cache loader to subsequently load the element again
2599

2600      if(sendNodeEvent) {
2601         notifyNodeVisisted(fqn);
2602      }
2603      else { // FIXME Bela did this so GUI view can refresh the view after node is evicted. But this breaks eviction policy, especially AOP!!!!
2604
if(eviction)
2605            notifyNodeEvicted(fqn);
2606         else
2607            notifyNodeModified(fqn); // todo: merge these 2 notifications back into 1 !
2608
}
2609
2610      // put undo-op in TX' undo-operations list (needed to rollback TX)
2611
if(tx != null && create_undo_ops) {
2612         tx_table.addUndoOperation(tx, undo_op);
2613      }
2614   }
2615
2616
2617   /**
2618     * Called by eviction policy provider. Note that eviction is done only in local mode,
2619     * that is, it doesn't replicate the node removal. This is will cause the replcation nodes
2620     * not synchronizing, but it is ok since user is supposed to add the node again when get is
2621     * null. After that, the contents will be in sync.
2622     * @param fqn Will remove everythign assoicated with this fqn.
2623     * @throws CacheException
2624     */

2625    public void _evict(Fqn fqn) throws CacheException {
2626       if(!exists(fqn)) return; // node does not exist. Maybe it has been recursively removed.
2627
// use remove method now if there is a child node. Otherwise, it is removed
2628
boolean create_undo_ops = false;
2629       boolean sendNodeEvent = false;
2630       boolean eviction=true;
2631      if(log.isTraceEnabled())
2632         log.trace("_evict(" + fqn + ")");
2633       if(hasChild(fqn)) {
2634          _removeData(null, fqn, create_undo_ops, sendNodeEvent, eviction);
2635       }
2636       else {
2637          _remove(null, fqn, create_undo_ops, sendNodeEvent, eviction);
2638       }
2639    }
2640
2641
2642    /**
2643     * Evicts a key/value pair from a node's attributes. Note that this is <em>local</em>, will not be replicated.
2644     * @param fqn
2645     * @param key
2646     * @throws CacheException
2647     */

2648// public void _evict(Fqn fqn, Object key) throws CacheException {
2649
// if(!exists(fqn)) return;
2650
// boolean create_undo_ops = false;
2651
// boolean sendNodeEvent = false;
2652
// boolean eviction=true;
2653
// _removeData(null, fqn, create_undo_ops, sendNodeEvent, eviction);
2654
// }
2655

2656
2657
2658
2659   /**
2660    * Compensating method to {@link #_remove(GlobalTransaction,Fqn,boolean)}. This
2661    * is package-private on purpose because it is only supposed to be called inside
2662    * the same VM (by {@link #rollback(GlobalTransaction)}).
2663    *
2664    * @param parent_fqn
2665    * @param child_name
2666    * @param old_node
2667    */

2668   public void _addChild(GlobalTransaction tx, Fqn parent_fqn, Object JavaDoc child_name, Node old_node)
2669           throws CacheException {
2670      if(log.isTraceEnabled())
2671         log.trace("_addChild(" + tx + ", \"" + parent_fqn + "\", \"" + child_name + "\")");
2672
2673      if(parent_fqn == null || child_name == null || old_node == null) {
2674         log.error("parent_fqn or child_name or node was null");
2675         return;
2676      }
2677      Node tmp=findNode(parent_fqn);
2678      if(tmp == null) {
2679         log.warn("node " + parent_fqn + " not found");
2680         return;
2681      }
2682
2683      tmp.addChild(child_name, old_node);
2684   }
2685
2686
2687
2688
2689   /**
2690    * Invoked by the ReplicationInterceptor of other nodes in the cluster, to replicate their changes across the
2691    * cluster. Needs to be forwarded to the ReplicationInterceptor in this interceptor chain.<br/>
2692    * This method will later be moved entirely into the ReplicationInterceptor.
2693    * @param method_call
2694    * @return
2695    * @throws Throwable
2696    */

2697   public Object JavaDoc _replicate(MethodCall method_call) throws Throwable JavaDoc {
2698      if(replication_handler != null) {
2699         return replication_handler.replicate(method_call);
2700      }
2701      else {
2702         throw new UnsupportedOperationException JavaDoc("no replication handler is installed");
2703      }
2704   }
2705
2706   public void _replicate(List method_calls) throws Throwable JavaDoc {
2707      if(replication_handler != null) {
2708         replication_handler.replicate(method_calls);
2709      }
2710      else {
2711         throw new UnsupportedOperationException JavaDoc("no replication handler is installed");
2712      }
2713   }
2714
2715   public void _releaseAllLocks(Fqn fqn) {
2716      Node n;
2717
2718      try {
2719         n=findNode(fqn);
2720         if(n == null) {
2721            log.error("releaseAllLocks(): node " + fqn + " not found");
2722            return;
2723         }
2724         n.releaseAllForce();
2725      }
2726      catch(Throwable JavaDoc t) {
2727         log.error("releaseAllLocks(): failed", t);
2728      }
2729   }
2730
2731
2732   public String JavaDoc _print(Fqn fqn) {
2733      try {
2734         Node n=findNode(fqn);
2735         if(n == null) return null;
2736         return n.toString();
2737      }
2738      catch(Throwable JavaDoc t) {
2739         return null;
2740      }
2741   }
2742
2743
2744   public void _lock(Fqn fqn, int lock_type, boolean recursive)
2745         throws TimeoutException, LockingException {
2746      log.warn("method _lock() should not be invoked on TreeCache");
2747   }
2748
2749   // todo: these methods can be removed once we move 2PC entirely into {Replication/Lock}Interceptor
2750
public void prepare(GlobalTransaction global_tx, List modifications, Address coord, boolean commit) {
2751      throw new UnsupportedOperationException JavaDoc("prepare() should not be called on TreeCache directly");
2752   }
2753
2754   public void commit(GlobalTransaction tx) {
2755      throw new UnsupportedOperationException JavaDoc("commit() should not be called on TreeCache directly");
2756   }
2757
2758   public void rollback(GlobalTransaction tx) {
2759      throw new UnsupportedOperationException JavaDoc("rollback() should not be called on TreeCache directly");
2760   }
2761
2762
2763   /* ----------------- End of Callbacks ---------------------- */
2764
2765
2766   public void addNode(GlobalTransaction gtx, Fqn node) {
2767      tx_table.addNode(gtx, node);
2768   }
2769
2770
2771
2772   /*-------------------- MessageListener ----------------------*/
2773
2774   class MessageListenerAdaptor implements MessageListener {
2775      final TreeCache cache;
2776      final Logger log; // Need this to run under jdk1.3
2777

2778      MessageListenerAdaptor(TreeCache cache, Logger log) {
2779         this.cache = cache;
2780         this.log = log;
2781      }
2782
2783      /**
2784       * Callback (no-op)
2785       *
2786       * @param msg
2787       */

2788      public void receive(Message msg) {
2789      }
2790
2791      /**
2792       * Return a copy of the current cache (tree). It actually returns a 2 element array of byte[], element 0
2793       * being the transient state (or null) and element 1 being the persistent state (or null)
2794       */

2795      public byte[] getState() {
2796         Object JavaDoc owner=null;
2797         boolean fetch_persistent_state=cache_loader != null &&
2798               cache_loader_shared == false &&
2799               cache_loader_fetch_persistent_state;
2800         byte[] transient_state=null;
2801         byte[] persistent_state=null;
2802         byte[][] states=new byte[2][];
2803         byte[] retval=null;
2804         boolean locked=false;
2805
2806         owner=getCurrentTransaction();
2807         if(owner == null) owner=Thread.currentThread();
2808
2809         states[0]=states[1]=null;
2810         try {
2811            if(cache_loader_fetch_transient_state) {
2812               log.info("locking the tree to obtain transient state");
2813               root.acquireAll(owner, state_fetch_timeout, Node.LOCK_TYPE_READ);
2814               locked=true;
2815               transient_state=Util.objectToByteBuffer(root);
2816               states[0]=transient_state;
2817               log.info("returning the transient state (" + transient_state.length + " bytes)");
2818            }
2819         }
2820         catch(Throwable JavaDoc t) {
2821            log.error("failed getting the transient state", t);
2822         }
2823         try {
2824            if(fetch_persistent_state) {
2825               if(!locked) {
2826                  log.info("locking the tree to obtain persistent state");
2827                  root.acquireAll(owner, state_fetch_timeout, Node.LOCK_TYPE_READ);
2828                  locked=true;
2829               }
2830               log.info("getting the persistent state");
2831               persistent_state=cache_loader.loadEntireState();
2832               states[1]=persistent_state;
2833               log.info("returning the persistent state (" + persistent_state.length + " bytes)");
2834            }
2835         }
2836         catch(Throwable JavaDoc t) {
2837            log.error("failed getting the persistent state", t);
2838         }
2839
2840         try {
2841            retval=Util.objectToByteBuffer(states);
2842            return retval;
2843         }
2844         catch(Throwable JavaDoc t) {
2845            log.error("failed serializing transient and persistent state", t);
2846            return retval;
2847         }
2848
2849         finally {
2850            root.releaseAll(owner);
2851         }
2852      }
2853
2854
2855      public void setState(byte[] new_state) {
2856         try {
2857            _setState(new_state);
2858         }
2859         finally {
2860            synchronized(stateLock) {
2861               // Notify wait that state has been set.
2862
stateLock.notifyAll();
2863               if(root != null)
2864                  root.releaseAllForce();
2865            }
2866         }
2867      }
2868
2869      /**
2870       * Set the cache (tree) to this value. The new_state is a byt[][] array, element 0 is the transient state
2871       * (or null) , and element 1 is the persistent state (or null)
2872       */

2873      void _setState(byte[] new_state) {
2874         Node new_root=null, old_root=null;
2875         Object JavaDoc obj;
2876         Object JavaDoc owner=null;
2877         byte[][] states=null;
2878         byte[] transient_state=null;
2879         byte[] persistent_state=null;
2880         boolean locked=false;
2881
2882         if(new_state == null) {
2883            log.info("new cache is null (maybe first member in cluster)");
2884            return;
2885         }
2886
2887         // 1. Unserialize the states into transient and persistent state
2888
try {
2889            log.info("received the state (size=" + new_state.length + " bytes)");
2890            states=(byte[][])Util.objectFromByteBuffer(new_state);
2891            transient_state=states[0];
2892            persistent_state=states[1];
2893            if(transient_state != null)
2894               log.info("transient state: " + transient_state.length + " bytes");
2895            if(persistent_state != null)
2896               log.info("persistent state: " + persistent_state.length + " bytes");
2897            owner=getCurrentTransaction();
2898            if(owner == null) owner=Thread.currentThread();
2899         }
2900         catch(Throwable JavaDoc t) {
2901            log.error("failed unserializing state", t);
2902         }
2903
2904         // 2. If transient state is available: set it (lock tree first)
2905
if(transient_state != null) {
2906            try {
2907               log.info("setting transient state");
2908               obj=Util.objectFromByteBuffer(transient_state);
2909               new_root=(Node)obj;
2910               new_root.setRecursiveTreeCacheInstance(cache); // need to set this at root and set it recursively
2911
log.info("locking the old tree");
2912               root.acquireAll(owner, state_fetch_timeout, Node.LOCK_TYPE_WRITE);
2913               locked=true;
2914               log.info("locking the old tree was successful");
2915               old_root=root;
2916               root=new_root;
2917               log.info("setting the transient state was successful");
2918               notifyAllNodesCreated(root);
2919            }
2920            catch(Throwable JavaDoc t) {
2921               log.error("failed setting transient state", t);
2922            }
2923         }
2924
2925         // 3. Set the persistent state
2926
if(persistent_state != null) {
2927            if(cache_loader == null) {
2928               log.error("cache loader is null, cannot set persistent state");
2929            }
2930            else {
2931               try {
2932                  if(!locked) {
2933                     log.info("locking the old tree");
2934                     root.acquireAll(owner, state_fetch_timeout, Node.LOCK_TYPE_WRITE);
2935                     old_root=root; // to unlock later
2936
locked=true;
2937                     root=new Node(SEPARATOR, Fqn.fromString(SEPARATOR), null, null, cache);
2938                     log.info("locking the old tree was successful");
2939                  }
2940                  log.info("setting the persistent state");
2941                  // cache_loader.remove(Fqn.fromString("/"));
2942
cache_loader.storeEntireState(persistent_state);
2943                  log.info("setting the persistent state was successful");
2944               }
2945               catch(Throwable JavaDoc t) {
2946                  log.error("failed setting persistent state", t);
2947               }
2948            }
2949         }
2950
2951         if(old_root != null) {
2952            log.info("forcing release of all locks in old tree");
2953            try {old_root.releaseAllForce();} catch(Throwable JavaDoc t) {log.error("failed releasing locks", t);}
2954         }
2955
2956         isStateSet=true;
2957      }
2958
2959
2960
2961   }
2962
2963
2964
2965   /*-------------------- End of MessageListener ----------------------*/
2966
2967
2968   /*----------------------- MembershipListener ------------------------*/
2969
2970   public void viewAccepted(View new_view) {
2971      Vector new_mbrs=new_view.getMembers();
2972
2973      // todo: if MergeView, fetch and reconcile state from coordinator
2974
// actually maybe this is best left up to the application ? we just notify them and let
2975
// the appl handle it ?
2976

2977      log.info("viewAccepted(): new members: " + new_mbrs);
2978      if(new_mbrs != null) {
2979         members.removeAllElements();
2980         members.addAll(new_view.getMembers());
2981         notifyViewChange(new_view);
2982      }
2983
2984      if(cache_loader_shared) {
2985         // boolean old_coord=coordinator;
2986
coordinator=determineCoordinator();
2987      }
2988   }
2989
2990
2991   /**
2992    * Called when a member is suspected
2993    */

2994   public void suspect(Address suspected_mbr) {
2995   }
2996
2997
2998   /**
2999    * Block sending and receiving of messages until viewAccepted() is called
3000    */

3001   public void block() {
3002   }
3003
3004   /*------------------- End of MembershipListener ----------------------*/
3005
3006
3007   /* ------------------------------ Private methods --------------------------- */
3008
3009   /**
3010    * Returns the transaction associated with the current thread. We get the initial context
3011    * and a reference to the TransactionManager to get the transaction. This method is used
3012    * by {@link #getCurrentTransaction()}
3013    *
3014    * @return
3015    */

3016   protected Transaction JavaDoc getLocalTransaction() {
3017      if(tm == null) {
3018         return null;
3019      }
3020      try {
3021         return tm.getTransaction();
3022      }
3023      catch(Throwable JavaDoc t) {
3024         return null;
3025      }
3026   }
3027
3028
3029
3030   
3031   /** Returns true if transaction is ACTIVE or PREPARING, false otherwise */
3032   boolean isValid(Transaction JavaDoc tx) {
3033      if(tx == null) return false;
3034      int status=-1;
3035      try {
3036         status=tx.getStatus();
3037         return status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARING;
3038      }
3039      catch(SystemException JavaDoc e) {
3040         log.error("failed getting transaction status", e);
3041         return false;
3042      }
3043   }
3044
3045
3046   /**
3047    * Get the transaction associated with the current thread and maps it onto a
3048    * {@link GlobalTransaction}. The mapping is maintained in the <tt>tx_map</tt> table.
3049    * If a local transaction exists, but doesn't yet have a mapping to a GlobalTransaction,
3050    * a new GlobalTransaction will be created and mapped to the local transaction.
3051    * Note that if a local transaction exists, but is not ACTIVE or PREPARING, null is returned too.
3052    * @return A GlobalTransaction, or null if no (local) transaction was associated with the current thread
3053    */

3054   public GlobalTransaction getCurrentTransaction() {
3055      Transaction JavaDoc tx;
3056
3057      if((tx=getLocalTransaction()) == null) { // no transaction is associated with the current thread
3058
return null;
3059      }
3060
3061      if(!isValid(tx)) { // we got a non-null transaction, but it is not active anymore
3062
int status=-1;
3063         try {status=tx.getStatus();} catch(SystemException JavaDoc e) {}
3064         log.warn("status is " + status + " (not ACTIVE or PREPARING); returning null)");
3065         return null;
3066      }
3067
3068      return getCurrentTransaction(tx);
3069   }
3070
3071   public GlobalTransaction getCurrentTransaction(Transaction JavaDoc tx) {
3072      synchronized(tx_table) {
3073         GlobalTransaction gtx=tx_table.get(tx);
3074         if(gtx == null) {
3075            Address addr=(Address)getLocalAddress();
3076            gtx=GlobalTransaction.create(addr);
3077            tx_table.put(tx, gtx);
3078            TransactionEntry ent=new TransactionEntry();
3079            ent.setTransaction(tx);
3080            tx_table.put(gtx, ent);
3081            if(log.isTraceEnabled())
3082               log.trace("created new GTX: " + gtx + ", local TX=" + tx);
3083         }
3084         return gtx;
3085      }
3086   }
3087
3088
3089
3090   /**
3091    * Invokes a method against this object. Contains the logger_ic for handling the various
3092    * use cases, e.g. mode (local, repl_async, repl_sync), transaction (yes or no) and
3093    * locking (yes or no).
3094    *
3095    * @param m
3096    * @return
3097    * @throws CacheException
3098    */

3099   protected Object JavaDoc invokeMethod(MethodCall m) throws CacheException {
3100      // if(log.isTraceEnabled()) log.trace("calling method " + m.getName());
3101
try {
3102         return interceptor_chain.invoke(m);
3103      }
3104      catch(Throwable JavaDoc t) {
3105         if(t instanceof CacheException)
3106            throw (CacheException)t;
3107         throw new NestedRuntimeException(t);
3108      }
3109   }
3110
3111
3112   /**
3113    * Finds a node given a fully qualified name
3114    * Whenever nodes are created, and the global transaction is not null, the created
3115    * nodes have to be added to the transaction's {@link TransactionEntry#nodes}
3116    * field.<br>
3117    * When a lock is acquired on a node, a reference to the lock has to be added to the list
3118    * of locked nodes in the {@link TransactionEntry#locks} field.
3119    * <p>This operation will also apply different locking to the tree nodes, depending on
3120    * <tt>operation_type</tt>. If it is <tt>read</tt> type, all nodes will be acquired with
3121    * read lock. Otherwise, the operation is <tt>write</tt> type, all parent nodes will be acquired
3122    * with read lock while the destination node acquires write lock.</p>
3123    *
3124    * @param fqn Fully qualified name for the corresponding node.
3125    * @return Node
3126    */

3127   private Node findNode(Fqn fqn) {
3128      Node n, child_node=null;
3129      Object JavaDoc child_name;
3130      int treeNodeSize;
3131      Fqn tmp_fqn=new Fqn();
3132
3133      if(fqn == null) return null;
3134      if((treeNodeSize=fqn.size()) == 0)
3135         return root;
3136
3137      n=root;
3138      for(int i=0; i < treeNodeSize; i++) {
3139         child_name=fqn.get(i);
3140         tmp_fqn=new Fqn(tmp_fqn, child_name);
3141         child_node=n.getChild(child_name);
3142         if(child_node == null)
3143            return null;
3144         n=child_node;
3145      }
3146      return child_node;
3147   }
3148
3149
3150
3151   public void notifyNodeCreated(Fqn fqn) {
3152      for(int i=0; i < listeners.size(); i++)
3153         ((TreeCacheListener)listeners.elementAt(i)).nodeCreated(fqn);
3154   }
3155
3156   public void notifyNodeLoaded(Fqn fqn) {
3157      for(int i=0; i < listeners.size(); i++)
3158         ((TreeCacheListener)listeners.elementAt(i)).nodeLoaded(fqn);
3159   }
3160
3161   protected void notifyNodeRemoved(Fqn fqn) {
3162      for(int i=0; i < listeners.size(); i++)
3163         ((TreeCacheListener)listeners.elementAt(i)).nodeRemoved(fqn);
3164   }
3165
3166   protected void notifyNodeEvicted(Fqn fqn) {
3167      for(int i=0; i < listeners.size(); i++)
3168         ((TreeCacheListener)listeners.elementAt(i)).nodeEvicted(fqn);
3169   }
3170
3171   protected void notifyNodeModified(Fqn fqn) {
3172      for(int i=0; i < listeners.size(); i++)
3173         ((TreeCacheListener)listeners.elementAt(i)).nodeModified(fqn);
3174   }
3175
3176   protected void notifyNodeVisisted(Fqn fqn) {
3177      for(int i=0; i < listeners.size(); i++)
3178         ((TreeCacheListener)listeners.elementAt(i)).nodeVisited(fqn);
3179   }
3180
3181   protected void notifyCacheStarted() {
3182      for(int i=0; i < listeners.size(); i++)
3183         ((TreeCacheListener)listeners.elementAt(i)).cacheStarted(this);
3184   }
3185
3186   protected void notifyCacheStopped() {
3187      for(int i=0; i < listeners.size(); i++)
3188         ((TreeCacheListener)listeners.elementAt(i)).cacheStopped(this);
3189   }
3190
3191   protected void notifyViewChange(View v) {
3192      for(int i=0; i < listeners.size(); i++)
3193         ((TreeCacheListener)listeners.elementAt(i)).viewChange(v);
3194   }
3195
3196   /**
3197    * Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is
3198    * initially retrieved (state transfer)
3199    */

3200   protected void notifyAllNodesCreated(Node curr) {
3201      Node n;
3202      Map JavaDoc children;
3203
3204      if(curr == null) return;
3205      notifyNodeCreated(curr.fqn);
3206      if((children=curr.getChildren()) != null) {
3207         for(Iterator it=children.values().iterator(); it.hasNext();) {
3208            n=(Node)it.next();
3209            notifyAllNodesCreated(n);
3210         }
3211      }
3212   }
3213
3214   protected String JavaDoc getDefaultProperties() {
3215      return "UDP(mcast_addr=224.0.0.36;mcast_port=55566;ip_ttl=32;" +
3216              "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" +
3217              "PING(timeout=1000;num_initial_members=2):" +
3218              "MERGE2(min_interval=5000;max_interval=10000):" +
3219              "FD_SOCK:" +
3220              "VERIFY_SUSPECT(timeout=1500):" +
3221              "pbcast.NAKACK(gc_lag=50;max_xmit_size=8192;retransmit_timeout=600,1200,2400,4800):" +
3222              "UNICAST(timeout=600,1200,2400,4800):" +
3223              "pbcast.STABLE(desired_avg_gossip=20000):" +
3224              "FRAG(frag_size=8192;down_thread=false;up_thread=false):" +
3225              "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" +
3226              "shun=false;print_local_addr=true):" +
3227              "pbcast.STATE_TRANSFER";
3228   }
3229
3230
3231   protected int string2Mode(String JavaDoc mode) {
3232      if(mode == null) return -1;
3233      String JavaDoc m=mode.toLowerCase().trim();
3234      if(m.equals("local"))
3235         return LOCAL;
3236      else
3237         if(m.equals("repl_async") || m.equals("repl-async"))
3238            return REPL_ASYNC;
3239         else
3240            if(m.equals("repl_sync") || m.equals("repl-sync"))
3241               return REPL_SYNC;
3242            else
3243               return -1;
3244   }
3245
3246
3247
3248}
3249
3250
3251
Popular Tags