1 
2 package org.jgroups.blocks;
3 
4 
5 import org.jgroups.*;
6 import org.jgroups.annotations.Unsupported;
7 import org.jgroups.jmx.JmxConfigurator;
8 import org.jgroups.logging.Log;
9 import org.jgroups.logging.LogFactory;
10 import org.jgroups.util.Util;
11 
12 import javax.management.MBeanServer;
13 import java.io.Serializable;
14 import java.util.*;
15 
16 
17 
18 
19 /**
20  * A tree-like structure that is replicated across several members. Updates will be multicast to all group
21  * members reliably and in the same order.
22  * @author Bela Ban Jan 17 2002
23  * @author <a href="mailto:aolias@yahoo.com">Alfonso Olias-Sanz</a>
24  */
25 @Unsupported
26 public class ReplicatedTree extends ReceiverAdapter {
27     public static final String SEPARATOR="/";
28     final static int INDENT=4;
29     Node root=new Node(SEPARATOR, SEPARATOR, null, null);
30     final Vector<ReplicatedTreeListener> listeners=new Vector<ReplicatedTreeListener>();
31     JChannel channel=null;
32     String groupname="ReplicatedTree-Group";
33     final Vector<Address> members=new Vector<Address>();
34     long state_fetch_timeout=10000;
35     boolean jmx=false;
36 
37     protected final Log log=LogFactory.getLog(this.getClass());
38 
39 
40     /** Whether or not to use remote calls. If false, all methods will be invoked directly on this
41      instance rather than sending a message to all replicas and only then invoking the method.
42      Useful for testing */
43     boolean remote_calls=true;
44     String props="udp.xml";
45 
46 	/** Determines when the updates have to be sent across the network, avoids sending unnecessary
47      * messages when there are no member in the group */
48 	private boolean send_message = false;
49 
50 
51 
52     public interface ReplicatedTreeListener {
nodeAdded(String fqn)53         void nodeAdded(String fqn);
54 
nodeRemoved(String fqn)55         void nodeRemoved(String fqn);
56 
nodeModified(String fqn)57         void nodeModified(String fqn);
58 
viewChange(View new_view)59         void viewChange(View new_view);  // might be MergeView after merging
60     }
61 
62 
63     /**
64      * Creates a channel with the given properties. Connects to the channel, then creates a PullPushAdapter
65      * and starts it
66      */
ReplicatedTree(String groupname, String props, long state_fetch_timeout)67     public ReplicatedTree(String groupname, String props, long state_fetch_timeout) throws Exception {
68         if(groupname != null)
69             this.groupname=groupname;
70         if(props != null)
71             this.props=props;
72         this.state_fetch_timeout=state_fetch_timeout;
73         channel=new JChannel(this.props);
74         channel.setReceiver(this);
75         channel.connect(this.groupname);
76         start();
77     }
78 
ReplicatedTree(String groupname, String props, long state_fetch_timeout, boolean jmx)79     public ReplicatedTree(String groupname, String props, long state_fetch_timeout, boolean jmx) throws Exception {
80         if(groupname != null)
81             this.groupname=groupname;
82         if(props != null)
83             this.props=props;
84         this.jmx=jmx;
85         this.state_fetch_timeout=state_fetch_timeout;
86         channel=new JChannel(this.props);
87         channel.setReceiver(this);
88         channel.connect(this.groupname);
89 
90         if(jmx) {
91             MBeanServer server=Util.getMBeanServer();
92             if(server == null)
93                 throw new Exception("No MBeanServers found; need to run with an MBeanServer present, or inside JDK 5");
94             JmxConfigurator.registerChannel(channel, server, "jgroups", channel.getClusterName() , true);
95         }
96         start();
97     }
98 
ReplicatedTree()99     public ReplicatedTree() {
100     }
101 
102 
103     /**
104      * Expects an already connected channel. Creates a PullPushAdapter and starts it
105      */
ReplicatedTree(JChannel channel)106     public ReplicatedTree(JChannel channel) throws Exception {
107         this.channel=channel;
108         channel.setReceiver(this);
109         start();
110     }
111 
112 
setRemoteCalls(boolean flag)113     public void setRemoteCalls(boolean flag) {
114         remote_calls=flag;
115     }
116 
setRootNode(Node n)117     public void setRootNode(Node n) {
118         root=n;
119     }
120 
getLocalAddress()121     public Address getLocalAddress() {
122         return channel != null? channel.getAddress() : null;
123     }
124 
getMembers()125     public Vector<Address> getMembers() {
126         return members;
127     }
128 
129 
130     /**
131      * Fetch the group state from the current coordinator. If successful, this will trigger setState().
132      */
fetchState(long timeout)133     public void fetchState(long timeout) throws ChannelClosedException, ChannelNotConnectedException {
134         boolean rc=channel.getState(null, timeout);
135         if(log.isInfoEnabled()) {
136             if(rc)
137                 log.info("state was retrieved successfully");
138             else
139                 log.info("state could not be retrieved (first member)");
140         }
141     }
142 
143 
addReplicatedTreeListener(ReplicatedTreeListener listener)144     public void addReplicatedTreeListener(ReplicatedTreeListener listener) {
145         if(!listeners.contains(listener))
146             listeners.addElement(listener);
147     }
148 
149 
removeReplicatedTreeListener(ReplicatedTreeListener listener)150     public void removeReplicatedTreeListener(ReplicatedTreeListener listener) {
151         listeners.removeElement(listener);
152     }
153 
154 
start()155     public final void start() throws Exception {
156         boolean rc=channel.getState(null, state_fetch_timeout);
157         if(log.isInfoEnabled()) {
158             if(rc)
159                 log.info("state was retrieved successfully");
160             else
161                 log.info("state could not be retrieved (first member)");
162         }
163     }
164 
165 
stop()166     public void stop() {
167         Util.close(channel);
168     }
169 
170 
171     /**
172      * Adds a new node to the tree and sets its data. If the node doesn not yet exist, it will be created.
173      * Also, parent nodes will be created if not existent. If the node already has data, then the new data
174      * will override the old one. If the node already existed, a nodeModified() notification will be generated.
175      * Otherwise a nodeCreated() motification will be emitted.
176      * @param fqn The fully qualified name of the new node
177      * @param data The new data. May be null if no data should be set in the node.
178      */
put(String fqn, HashMap data)179     public void put(String fqn, HashMap data) {
180         if(!remote_calls) {
181             _put(fqn, data);
182             return;
183         }
184 
185 		//Changes done by <aos>
186 		//if true, propagate action to the group
187         if(send_message == true) {
188             if(channel == null) {
189                 if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request");
190                 return;
191             }
192             try {
193                 channel.send(
194                         new Message(
195                                 null,
196                                 null,
197                                 new Request(Request.PUT, fqn, data)));
198             }
199             catch(Exception ex) {
200                 if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex);
201             }
202         }
203         else {
204             _put(fqn, data);
205         }
206     }
207 
208 
209     /**
210      * Adds a key and value to a given node. If the node doesn't exist, it will be created. If the node
211      * already existed, a nodeModified() notification will be generated. Otherwise a
212      * nodeCreated() motification will be emitted.
213      * @param fqn The fully qualified name of the node
214      * @param key The key
215      * @param value The value
216      */
put(String fqn, String key, Object value)217     public void put(String fqn, String key, Object value) {
218         if(!remote_calls) {
219             _put(fqn, key, value);
220             return;
221         }
222 
223         //Changes done by <aos>
224         //if true, propagate action to the group
225         if(send_message == true) {
226 
227             if(channel == null) {
228                 if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request");
229                 return;
230             }
231             try {
232                 channel.send(
233                         new Message(
234                                 null,
235                                 null,
236                                 new Request(Request.PUT, fqn, key, value)));
237             }
238             catch(Exception ex) {
239                 if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex);
240             }
241         }
242         else {
243             _put(fqn, key, value);
244         }
245     }
246 
247 
248     /**
249      * Removes the node from the tree.
250      * @param fqn The fully qualified name of the node.
251      */
remove(String fqn)252     public void remove(String fqn) {
253         if(!remote_calls) {
254             _remove(fqn);
255             return;
256         }
257 		//Changes done by <aos>
258 		//if true, propagate action to the group
259         if(send_message == true) {
260             if(channel == null) {
261                 if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request");
262                 return;
263             }
264             try {
265                 channel.send(
266                         new Message(null, null, new Request(Request.REMOVE, fqn)));
267             }
268             catch(Exception ex) {
269                 if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex);
270             }
271         }
272         else {
273             _remove(fqn);
274         }
275     }
276 
277 
278     /**
279      * Removes <code>key</code> from the node's hashmap
280      * @param fqn The fullly qualified name of the node
281      * @param key The key to be removed
282      */
remove(String fqn, String key)283     public void remove(String fqn, String key) {
284         if(!remote_calls) {
285             _remove(fqn, key);
286             return;
287         }
288 		//Changes done by <aos>
289 		//if true, propagate action to the group
290         if(send_message == true) {
291             if(channel == null) {
292                 if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request");
293                 return;
294             }
295             try {
296                 channel.send(
297                         new Message(
298                                 null,
299                                 null,
300                                 new Request(Request.REMOVE, fqn, key)));
301             }
302             catch(Exception ex) {
303                 if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex);
304             }
305         }
306         else {
307             _remove(fqn, key);
308         }
309     }
310 
311 
312     /**
313      * Checks whether a given node exists in the tree
314      * @param fqn The fully qualified name of the node
315      * @return boolean Whether or not the node exists
316      */
exists(String fqn)317     public boolean exists(String fqn) {
318         if(fqn == null) return false;
319         return findNode(fqn) != null;
320     }
321 
322 
323     /**
324      * Gets the keys of the <code>data</code> map. Returns all keys as Strings. Returns null if node
325      * does not exist.
326      * @param fqn The fully qualified name of the node
327      * @return Set A set of keys (as Strings)
328      */
getKeys(String fqn)329     public Set getKeys(String fqn) {
330         Node n=findNode(fqn);
331         Map data;
332 
333         if(n == null) return null;
334         data=n.getData();
335         if(data == null) return null;
336         return data.keySet();
337     }
338 
339 
340     /**
341      * Finds a node given its name and returns the value associated with a given key in its <code>data</code>
342      * map. Returns null if the node was not found in the tree or the key was not found in the hashmap.
343      * @param fqn The fully qualified name of the node.
344      * @param key The key.
345      */
get(String fqn, String key)346     public Object get(String fqn, String key) {
347         Node n=findNode(fqn);
348 
349         if(n == null) return null;
350         return n.getData(key);
351     }
352 
353 
354     /**
355      * Returns the data hashmap for a given node. This method can only be used by callers that are inside
356      * the same package. The reason is that callers must not modify the return value, as these modifications
357      * would not be replicated, thus rendering the replicas inconsistent.
358      * @param fqn The fully qualified name of the node
359      * @return HashMap The data hashmap for the given node
360      */
get(String fqn)361     Map<String,Object> get(String fqn) {
362         Node n=findNode(fqn);
363 
364         if(n == null) return null;
365         return n.getData();
366     }
367 
368 
369     /**
370      * Prints a representation of the node defined by <code>fqn</code>. Output includes name, fqn and
371      * data.
372      */
print(String fqn)373     public String print(String fqn) {
374         Node n=findNode(fqn);
375         if(n == null) return null;
376         return n.toString();
377     }
378 
379 
380     /**
381      * Returns all children of a given node
382      * @param fqn The fully qualified name of the node
383      * @return Set A list of child names (as Strings)
384      */
getChildrenNames(String fqn)385     public Set getChildrenNames(String fqn) {
386         Node n=findNode(fqn);
387         Map m;
388 
389         if(n == null) return null;
390         m=n.getChildren();
391         if(m != null)
392             return m.keySet();
393         else
394             return null;
395     }
396 
397 
toString()398     public String toString() {
399         StringBuilder sb=new StringBuilder();
400         int indent=0;
401         Map children;
402 
403         children=root.getChildren();
404         if(children != null && children.size() > 0) {
405             Collection nodes=children.values();
406             for(Iterator it=nodes.iterator(); it.hasNext();) {
407                 ((Node)it.next()).print(sb, indent);
408                 sb.append('\n');
409             }
410         }
411         else
412             sb.append(SEPARATOR);
413         return sb.toString();
414     }
415 
416    /**
417     * Returns the name of the group that the DistributedTree is connected to
418     * @return String
419     */
getGroupName()420     public String  getGroupName()           {return groupname;}
421 
422    /**
423     * Returns the Channel the DistributedTree is connected to
424     * @return Channel
425     */
getChannel()426     public Channel getChannel()             {return channel;}
427 
428    /**
429     * Returns the number of current members joined to the group
430     * @return int
431     */
getGroupMembersNumber()432     public int getGroupMembersNumber()			{return members.size();}
433 
434 
435 
436 
437     /* --------------------- Callbacks -------------------------- */
438 
439 
_put(String fqn, HashMap data)440     public void _put(String fqn, HashMap data) {
441         Node n;
442         StringHolder child_name=new StringHolder();
443         boolean child_exists=false;
444 
445         if(fqn == null) return;
446         n=findParentNode(fqn, child_name, true); // create all nodes if they don't exist
447         if(child_name.getValue() != null) {
448             child_exists=n.childExists(child_name.getValue());
449             n.createChild(child_name.getValue(), fqn, n, data);
450         }
451         else {
452             child_exists=true;
453             n.setData(data);
454         }
455         if(child_exists)
456             notifyNodeModified(fqn);
457         else
458             notifyNodeAdded(fqn);
459     }
460 
461 
_put(String fqn, String key, Object value)462     public void _put(String fqn, String key, Object value) {
463         Node n;
464         StringHolder child_name=new StringHolder();
465         boolean child_exists=false;
466 
467         if(fqn == null || key == null || value == null) return;
468         n=findParentNode(fqn, child_name, true);
469         if(child_name.getValue() != null) {
470             child_exists=n.childExists(child_name.getValue());
471             n.createChild(child_name.getValue(), fqn, n, key, value);
472         }
473         else {
474             child_exists=true;
475             n.setData(key, value);
476         }
477         if(child_exists)
478             notifyNodeModified(fqn);
479         else
480             notifyNodeAdded(fqn);
481     }
482 
483 
_remove(String fqn)484     public void _remove(String fqn) {
485         Node n;
486         StringHolder child_name=new StringHolder();
487 
488         if(fqn == null) return;
489         if(fqn.equals(SEPARATOR)) {
490             root.removeAll();
491             notifyNodeRemoved(fqn);
492             return;
493         }
494         n=findParentNode(fqn, child_name, false);
495         if(n == null) return;
496         n.removeChild(child_name.getValue(), fqn);
497         notifyNodeRemoved(fqn);
498     }
499 
500 
_remove(String fqn, String key)501     public void _remove(String fqn, String key) {
502         Node n;
503 
504         if(fqn == null || key == null) return;
505         n=findNode(fqn);
506         if(n != null)
507             n.removeData(key);
508     }
509 
510 
_removeData(String fqn)511     public void _removeData(String fqn) {
512         Node n;
513 
514         if(fqn == null) return;
515         n=findNode(fqn);
516         if(n != null)
517             n.removeData();
518     }
519 
520 
521     /* ----------------- End of  Callbacks ---------------------- */
522 
523 
524 
525 
526 
527 
528     /*-------------------- MessageListener ----------------------*/
529 
530     /** Callback. Process the contents of the message; typically an _add() or _set() request */
receive(Message msg)531     public void receive(Message msg) {
532         Request req=null;
533 
534         if(msg == null || msg.getLength() == 0)
535             return;
536         try {
537             req=(Request)msg.getObject();
538 
539             String fqn=req.fqn;
540             switch(req.type) {
541                 case Request.PUT:
542                     if(req.key != null && req.value != null)
543                         _put(fqn, req.key, req.value);
544                     else
545                         _put(fqn, req.data);
546                     break;
547                 case Request.REMOVE:
548                     if(req.key != null)
549                         _remove(fqn, req.key);
550                     else
551                         _remove(fqn);
552                     break;
553                 default:
554                     if(log.isErrorEnabled()) log.error("type " + req.type + " unknown");
555                     break;
556             }
557         }
558         catch(Exception ex) {
559             if(log.isErrorEnabled()) log.error("failed unmarshalling request: " + ex);
560         }
561     }
562 
563     /** Return a copy of the current cache (tree) */
getState()564     public byte[] getState() {
565         try {
566             return Util.objectToByteBuffer(root.clone());
567         }
568         catch(Throwable ex) {
569             if(log.isErrorEnabled()) log.error("exception returning cache: " + ex);
570             return null;
571         }
572     }
573 
574     /** Set the cache (tree) to this value */
setState(byte[] new_state)575     public void setState(byte[] new_state) {
576         Node new_root=null;
577         Object obj;
578 
579         if(new_state == null) {
580             if(log.isInfoEnabled()) log.info("new cache is null");
581             return;
582         }
583         try {
584             obj=Util.objectFromByteBuffer(new_state);
585             new_root=(Node)((Node)obj).clone();
586             root=new_root;
587             notifyAllNodesCreated(root);
588         }
589         catch(Throwable ex) {
590             if(log.isErrorEnabled()) log.error("could not set cache: " + ex);
591         }
592     }
593 
594     /*-------------------- End of MessageListener ----------------------*/
595 
596 
597 
598 
599 
600     /*----------------------- MembershipListener ------------------------*/
601 
viewAccepted(View new_view)602     public void viewAccepted(View new_view) {
603         Vector<Address> new_mbrs=new_view.getMembers();
604 
605         // todo: if MergeView, fetch and reconcile state from coordinator
606         // actually maybe this is best left up to the application ? we just notify them and let the appl handle it ?
607 
608         if(new_mbrs != null) {
609             notifyViewChange(new_view);
610             members.removeAllElements();
611             for(int i=0; i < new_mbrs.size(); i++)
612                 members.addElement(new_mbrs.elementAt(i));
613         }
614 		//if size is bigger than one, there are more peers in the group
615 		//otherwise there is only one server.
616         send_message=members.size() > 1;
617     }
618 
619     /*------------------- End of MembershipListener ----------------------*/
620 
621 
622 
623 
624     /**
625      * Find the node just <em>above</em> the one indicated by <code>fqn</code>. This is needed in many cases,
626      * e.g. to add a new node or remove an existing node.
627      * @param fqn The fully qualified name of the node.
628      * @param child_name Will be filled with the name of the child when this method returns. The child name
629      *                   is the last relative name of the <code>fqn</code>, e.g. in "/a/b/c" it would be "c".
630      * @param create_if_not_exists Create parent nodes along the way if they don't exist. Otherwise, this method
631      *                             will return when a node cannot be found.
632      */
findParentNode(String fqn, StringHolder child_name, boolean create_if_not_exists)633     Node findParentNode(String fqn, StringHolder child_name, boolean create_if_not_exists) {
634         Node curr=root, node;
635         StringTokenizer tok;
636         String name;
637         StringBuilder sb=null;
638 
639         if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn))
640             return curr;
641 
642         sb=new StringBuilder();
643         tok=new StringTokenizer(fqn, SEPARATOR);
644         while(tok.countTokens() > 1) {
645             name=tok.nextToken();
646             sb.append(SEPARATOR).append(name);
647             node=curr.getChild(name);
648             if(node == null && create_if_not_exists)
649                 node=curr.createChild(name, sb.toString(), null, null);
650             if(node == null)
651                 return null;
652             else
653                 curr=node;
654         }
655 
656         if(tok.countTokens() > 0 && child_name != null)
657             child_name.setValue(tok.nextToken());
658         return curr;
659     }
660 
661 
662     /**
663      * Returns the node at fqn. This method should not be used by clients (therefore it is package-private):
664      * it is only used internally (for navigation). C++ 'friend' would come in handy here...
665      * @param fqn The fully qualified name of the node
666      * @return Node The node at fqn
667      */
findNode(String fqn)668     Node findNode(String fqn) {
669         StringHolder sh=new StringHolder();
670         Node n=findParentNode(fqn, sh, false);
671         String child_name=sh.getValue();
672 
673         if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn))
674             return root;
675 
676         if(n == null || child_name == null)
677             return null;
678         else
679             return n.getChild(child_name);
680     }
681 
682 
notifyNodeAdded(String fqn)683     void notifyNodeAdded(String fqn) {
684         for(int i=0; i < listeners.size(); i++)
685             listeners.elementAt(i).nodeAdded(fqn);
686     }
687 
notifyNodeRemoved(String fqn)688     void notifyNodeRemoved(String fqn) {
689         for(int i=0; i < listeners.size(); i++)
690             listeners.elementAt(i).nodeRemoved(fqn);
691     }
692 
notifyNodeModified(String fqn)693     void notifyNodeModified(String fqn) {
694         for(int i=0; i < listeners.size(); i++)
695             listeners.elementAt(i).nodeModified(fqn);
696     }
697 
notifyViewChange(View v)698     void notifyViewChange(View v) {
699         for(int i=0; i < listeners.size(); i++)
700             listeners.elementAt(i).viewChange(v);
701     }
702 
703     /** Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is
704      initially retrieved (state transfer) */
notifyAllNodesCreated(Node curr)705     void notifyAllNodesCreated(Node curr) {
706         Node n;
707         Map children;
708 
709         if(curr == null) return;
710         notifyNodeAdded(curr.fqn);
711         if((children=curr.getChildren()) != null) {
712             for(Iterator it=children.values().iterator(); it.hasNext();) {
713                 n=(Node)it.next();
714                 notifyAllNodesCreated(n);
715             }
716         }
717     }
718 
719 
720     public static class Node implements Serializable {
721         String name=null;     // relative name (e.g. "Security")
722         String fqn=null;      // fully qualified name (e.g. "/federations/fed1/servers/Security")
723         Node parent=null;   // parent node
724         TreeMap<String,Node> children=null; // keys: child name, value: Node
725         Map<String,Object> data=null;     // data for current node
726         private static final long serialVersionUID = -3077676554440038890L;
727 
728 
Node(String child_name, String fqn, Node parent, Map<String,Object> data)729         private Node(String child_name, String fqn, Node parent, Map<String,Object> data) {
730             name=child_name;
731             this.fqn=fqn;
732             this.parent=parent;
733             if(data != null) this.data=(HashMap<String,Object>)((HashMap)data).clone();
734         }
735 
Node(String child_name, String fqn, Node parent, String key, Object value)736         private Node(String child_name, String fqn, Node parent, String key, Object value) {
737             name=child_name;
738             this.fqn=fqn;
739             this.parent=parent;
740             if(data == null) data=new HashMap<String,Object>();
741             data.put(key, value);
742         }
743 
setData(Map data)744         void setData(Map data) {
745             if(data == null) return;
746             if(this.data == null)
747                 this.data=new HashMap<String,Object>();
748             this.data.putAll(data);
749         }
750 
setData(String key, Object value)751         void setData(String key, Object value) {
752             if(this.data == null)
753                 this.data=new HashMap<String,Object>();
754             this.data.put(key, value);
755         }
756 
getData()757         Map<String,Object> getData() {
758             return data;
759         }
760 
getData(String key)761         Object getData(String key) {
762             return data != null? data.get(key) : null;
763         }
764 
765 
childExists(String child_name)766         boolean childExists(String child_name) {
767             return child_name != null && children != null && children.containsKey(child_name);
768         }
769 
770 
createChild(String child_name, String fqn, Node parent, HashMap<String,Object> data)771         Node createChild(String child_name, String fqn, Node parent, HashMap<String,Object> data) {
772             Node child=null;
773 
774             if(child_name == null) return null;
775             if(children == null) children=new TreeMap<String,Node>();
776             child=children.get(child_name);
777             if(child != null)
778                 child.setData(data);
779             else {
780                 child=new Node(child_name, fqn, parent, data);
781                 children.put(child_name, child);
782             }
783             return child;
784         }
785 
createChild(String child_name, String fqn, Node parent, String key, Object value)786         Node createChild(String child_name, String fqn, Node parent, String key, Object value) {
787             Node child=null;
788 
789             if(child_name == null) return null;
790             if(children == null) children=new TreeMap<String,Node>();
791             child=(Node)children.get(child_name);
792             if(child != null)
793                 child.setData(key, value);
794             else {
795                 child=new Node(child_name, fqn, parent, key, value);
796                 children.put(child_name, child);
797             }
798             return child;
799         }
800 
801 
getChild(String child_name)802         Node getChild(String child_name) {
803             return child_name == null? null : children == null? null : (Node)children.get(child_name);
804         }
805 
getChildren()806         Map<String,Node> getChildren() {
807             return children;
808         }
809 
removeData(String key)810         void removeData(String key) {
811             if(data != null)
812                 data.remove(key);
813         }
814 
removeData()815         void removeData() {
816             if(data != null)
817                 data.clear();
818         }
819 
removeChild(String child_name, String fqn)820         void removeChild(String child_name, String fqn) {
821             if(child_name != null && children != null && children.containsKey(child_name)) {
822                 children.remove(child_name);
823             }
824         }
825 
removeAll()826         void removeAll() {
827             if(children != null)
828                 children.clear();
829         }
830 
print(StringBuilder sb, int indent)831         void print(StringBuilder sb, int indent) {
832             printIndent(sb, indent);
833             sb.append(SEPARATOR).append(name);
834             if(children != null && !children.isEmpty()) {
835                 Collection values=children.values();
836                 for(Iterator it=values.iterator(); it.hasNext();) {
837                     sb.append('\n');
838                     ((Node)it.next()).print(sb, indent + INDENT);
839                 }
840             }
841         }
842 
printIndent(StringBuilder sb, int indent)843         static void printIndent(StringBuilder sb, int indent) {
844             if(sb != null) {
845                 for(int i=0; i < indent; i++)
846                     sb.append(' ');
847             }
848         }
849 
850 
toString()851         public String toString() {
852             StringBuilder sb=new StringBuilder();
853             if(name != null) sb.append("\nname=" + name);
854             if(fqn != null) sb.append("\nfqn=" + fqn);
855             if(data != null) sb.append("\ndata=" + data);
856             return sb.toString();
857         }
858 
859 
clone()860         public Object clone() throws CloneNotSupportedException {
861             Node n=new Node(name, fqn, parent != null? (Node)parent.clone() : null, data);
862             if(children != null) n.children=(TreeMap)children.clone();
863             return n;
864         }
865 
866     }
867 
868 
869     private static class StringHolder {
870         String s=null;
871 
StringHolder()872         private StringHolder() {
873         }
874 
setValue(String s)875         void setValue(String s) {
876             this.s=s;
877         }
878 
getValue()879         String getValue() {
880             return s;
881         }
882     }
883 
884 
885     /**
886      * Class used to multicast add(), remove() and set() methods to all members.
887      */
888     private static class Request implements Serializable {
889         static final int PUT=1;
890         static final int REMOVE=2;
891 
892         int type=0;
893         String fqn=null;
894         String key=null;
895         Object value=null;
896         HashMap data=null;
897         private static final long serialVersionUID = 7772753222127676782L;
898 
Request(int type, String fqn)899         private Request(int type, String fqn) {
900             this.type=type;
901             this.fqn=fqn;
902         }
903 
Request(int type, String fqn, HashMap data)904         private Request(int type, String fqn, HashMap data) {
905             this(type, fqn);
906             this.data=data;
907         }
908 
Request(int type, String fqn, String key)909         private Request(int type, String fqn, String key) {
910             this(type, fqn);
911             this.key=key;
912         }
913 
Request(int type, String fqn, String key, Object value)914         private Request(int type, String fqn, String key, Object value) {
915             this(type, fqn);
916             this.key=key;
917             this.value=value;
918         }
919 
toString()920         public String toString() {
921             StringBuilder sb=new StringBuilder();
922             sb.append(type2String(type)).append(" (");
923             if(fqn != null) sb.append(" fqn=" + fqn);
924             switch(type) {
925                 case PUT:
926                     if(data != null) sb.append(", data=" + data);
927                     if(key != null) sb.append(", key=" + key);
928                     if(value != null) sb.append(", value=" + value);
929                     break;
930                 case REMOVE:
931                     if(key != null) sb.append(", key=" + key);
932                     break;
933                 default:
934                     break;
935             }
936             sb.append(')');
937             return sb.toString();
938         }
939 
type2String(int t)940         static String type2String(int t) {
941             switch(t) {
942                 case PUT:
943                     return "PUT";
944                 case REMOVE:
945                     return "REMOVE";
946                 default:
947                     return "UNKNOWN";
948             }
949         }
950 
951     }
952 
953 
main(String[] args)954     public static void main(String[] args) {
955 
956 
957         ReplicatedTree tree=null;
958         HashMap m=new HashMap();
959         String props;
960 
961         props="UDP(mcast_addr=224.0.0.36;mcast_port=55566;ip_ttl=32;" +
962                 "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" +
963                 "PING(timeout=2000;num_initial_members=3):" +
964                 "MERGE2(min_interval=5000;max_interval=10000):" +
965                 "FD_SOCK:" +
966                 "VERIFY_SUSPECT(timeout=1500):" +
967                 "pbcast.STABLE(desired_avg_gossip=20000):" +
968                 "pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800):" +
969                 "UNICAST(timeout=5000):" +
970                 "FRAG(frag_size=16000;down_thread=false;up_thread=false):" +
971                 "pbcast.GMS(join_timeout=5000;" +
972                 "print_local_addr=true):" +
973                 "pbcast.STATE_TRANSFER";
974         // "PERF(details=true)";
975 
976         try {
977 
978             tree=new ReplicatedTree(null, props, 10000);
979             // tree.setRemoteCalls(false);
980             tree.addReplicatedTreeListener(new MyListener());
981             tree.put("/a/b/c", null);
982             tree.put("/a/b/c1", null);
983             tree.put("/a/b/c2", null);
984             tree.put("/a/b1/chat", null);
985             tree.put("/a/b1/chat2", null);
986             tree.put("/a/b1/chat5", null);
987             System.out.println(tree);
988             m.put("name", "Bela Ban");
989             m.put("age", new Integer(36));
990             m.put("cube", "240-17");
991             tree.put("/a/b/c", m);
992             System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c"));
993             tree.put("/a/b/c", "age", new Integer(37));
994             System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c"));
995             tree.remove("/a/b");
996             System.out.println(tree);
997         }
998         catch(Exception ex) {
999             System.err.println(ex);
1000         }
1001     }
1002 
1003 
1004     static class MyListener implements ReplicatedTreeListener {
1005 
nodeAdded(String fqn)1006         public void nodeAdded(String fqn) {
1007             System.out.println("** node added: " + fqn);
1008         }
1009 
nodeRemoved(String fqn)1010         public void nodeRemoved(String fqn) {
1011             System.out.println("** node removed: " + fqn);
1012         }
1013 
nodeModified(String fqn)1014         public void nodeModified(String fqn) {
1015             System.out.println("** node modified: " + fqn);
1016         }
1017 
viewChange(View new_view)1018         public void viewChange(View new_view) {
1019             System.out.println("** view change: " + new_view);
1020         }
1021 
1022     }
1023 
1024 
1025 }
1026 
1027 
1028