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