001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.cluster.mcast;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021:
022: /**
023: * A <b>membership</b> implementation using simple multicast.
024: * This is the representation of a multicast membership.
025: * This class is responsible for maintaining a list of active cluster nodes in the cluster.
026: * If a node fails to send out a heartbeat, the node will be dismissed.
027: *
028: * @author Filip Hanik
029: * @version $Revision: 1.5 $, $Date: 2004/05/26 16:35:59 $
030: */
031:
032: public class McastMembership {
033: /**
034: * The name of this membership, has to be the same as the name for the local
035: * member
036: */
037: protected String name;
038: /**
039: * A map of all the members in the cluster.
040: */
041: protected HashMap map = new java.util.HashMap();
042:
043: /**
044: * Constructs a new membership
045: * @param myName - has to be the name of the local member. Used to filter the local member from the cluster membership
046: */
047: public McastMembership(String myName) {
048: name = myName;
049: }
050:
051: /**
052: * Reset the membership and start over fresh.
053: * Ie, delete all the members and wait for them to ping again and join this membership
054: */
055: public synchronized void reset() {
056: map.clear();
057: }
058:
059: /**
060: * Notify the membership that this member has announced itself.
061: *
062: * @param m - the member that just pinged us
063: * @return - true if this member is new to the cluster, false otherwise.
064: * @return - false if this member is the local member.
065: */
066: public synchronized boolean memberAlive(McastMember m) {
067: boolean result = false;
068: //ignore ourselves
069: if (m.getName().equals(name))
070: return result;
071:
072: //return true if the membership has changed
073: MbrEntry entry = (MbrEntry) map.get(m.getName());
074: if (entry == null) {
075: entry = new MbrEntry(m);
076: map.put(m.getName(), entry);
077: result = true;
078: } else {
079: //update the member alive time
080: entry.getMember()
081: .setMemberAliveTime(m.getMemberAliveTime());
082: }//end if
083: entry.accessed();
084: return result;
085: }
086:
087: /**
088: * Runs a refresh cycle and returns a list of members that has expired.
089: * This also removes the members from the membership, in such a way that
090: * getMembers() = getMembers() - expire()
091: * @param maxtime - the max time a member can remain unannounced before it is considered dead.
092: * @return the list of expired members
093: */
094: public synchronized McastMember[] expire(long maxtime) {
095: MbrEntry[] members = getMemberEntries();
096: java.util.ArrayList list = new java.util.ArrayList();
097: for (int i = 0; i < members.length; i++) {
098: MbrEntry entry = members[i];
099: if (entry.hasExpired(maxtime)) {
100: list.add(entry.getMember());
101: }//end if
102: }//while
103: McastMember[] result = new McastMember[list.size()];
104: list.toArray(result);
105: for (int j = 0; j < result.length; j++)
106: map.remove(result[j].getName());
107: return result;
108:
109: }//expire
110:
111: /**
112: * Returning a list of all the members in the membership
113: */
114: public synchronized McastMember[] getMembers() {
115: McastMember[] result = new McastMember[map.size()];
116: java.util.Iterator i = map.entrySet().iterator();
117: int pos = 0;
118: while (i.hasNext())
119: result[pos++] = ((MbrEntry) ((java.util.Map.Entry) i.next())
120: .getValue()).getMember();
121: return result;
122: }
123:
124: protected synchronized MbrEntry[] getMemberEntries() {
125: MbrEntry[] result = new MbrEntry[map.size()];
126: java.util.Iterator i = map.entrySet().iterator();
127: int pos = 0;
128: while (i.hasNext())
129: result[pos++] = ((MbrEntry) ((java.util.Map.Entry) i.next())
130: .getValue());
131: return result;
132: }
133:
134: /**
135: * Inner class that represents a member entry
136: */
137: protected static class MbrEntry {
138:
139: protected McastMember mbr;
140: protected long lastHeardFrom;
141:
142: public MbrEntry(McastMember mbr) {
143: this .mbr = mbr;
144: }
145:
146: /**
147: * Indicate that this member has been accessed.
148: */
149: public void accessed() {
150: lastHeardFrom = System.currentTimeMillis();
151: }
152:
153: /**
154: * Return the actual McastMember object
155: */
156: public McastMember getMember() {
157: return mbr;
158: }
159:
160: /**
161: * Check if this dude has expired
162: * @param maxtime The time threshold
163: */
164: public boolean hasExpired(long maxtime) {
165: long delta = System.currentTimeMillis() - lastHeardFrom;
166: return delta > maxtime;
167: }
168: }//MbrEntry
169: }
|