001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.tribes.membership;
019:
020: import java.util.Properties;
021:
022: import org.apache.catalina.tribes.Member;
023: import org.apache.catalina.tribes.MembershipListener;
024: import org.apache.catalina.tribes.MembershipService;
025: import org.apache.catalina.tribes.util.StringManager;
026: import org.apache.catalina.tribes.util.UUIDGenerator;
027: import java.io.IOException;
028:
029: /**
030: * A <b>membership</b> implementation using simple multicast.
031: * This is the representation of a multicast membership service.
032: * This class is responsible for maintaining a list of active cluster nodes in the cluster.
033: * If a node fails to send out a heartbeat, the node will be dismissed.
034: *
035: * @author Filip Hanik
036: * @version $Revision: 522651 $, $Date: 2007-03-27 00:24:32 +0200 (mar., 27 mars 2007) $
037: */
038:
039: public class McastService implements MembershipService,
040: MembershipListener {
041:
042: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
043: .getLog(McastService.class);
044:
045: /**
046: * The string manager for this package.
047: */
048: protected StringManager sm = StringManager
049: .getManager(Constants.Package);
050:
051: /**
052: * The descriptive information about this implementation.
053: */
054: private static final String info = "McastService/2.1";
055:
056: /**
057: * The implementation specific properties
058: */
059: protected Properties properties = new Properties();
060: /**
061: * A handle to the actual low level implementation
062: */
063: protected McastServiceImpl impl;
064: /**
065: * A membership listener delegate (should be the cluster :)
066: */
067: protected MembershipListener listener;
068: /**
069: * The local member
070: */
071: protected MemberImpl localMember;
072: private int mcastSoTimeout;
073: private int mcastTTL;
074:
075: protected byte[] payload;
076:
077: protected byte[] domain;
078:
079: /**
080: * Create a membership service.
081: */
082: public McastService() {
083: //default values
084: properties.setProperty("mcastPort", "45564");
085: properties.setProperty("mcastAddress", "228.0.0.4");
086: properties.setProperty("memberDropTime", "3000");
087: properties.setProperty("mcastFrequency", "500");
088:
089: }
090:
091: /**
092: * Return descriptive information about this implementation and the
093: * corresponding version number, in the format
094: * <code><description>/<version></code>.
095: */
096: public String getInfo() {
097: return (info);
098: }
099:
100: /**
101: *
102: * @param properties
103: * <BR/>All are required<BR />
104: * 1. mcastPort - the port to listen to<BR>
105: * 2. mcastAddress - the mcast group address<BR>
106: * 4. bindAddress - the bind address if any - only one that can be null<BR>
107: * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
108: * 6. mcastFrequency - the frequency of sending messages<BR>
109: * 7. tcpListenPort - the port this member listens to<BR>
110: * 8. tcpListenHost - the bind address of this member<BR>
111: * @exception java.lang.IllegalArgumentException if a property is missing.
112: */
113: public void setProperties(Properties properties) {
114: hasProperty(properties, "mcastPort");
115: hasProperty(properties, "mcastAddress");
116: hasProperty(properties, "memberDropTime");
117: hasProperty(properties, "mcastFrequency");
118: hasProperty(properties, "tcpListenPort");
119: hasProperty(properties, "tcpListenHost");
120: this .properties = properties;
121: }
122:
123: /**
124: * Return the properties, see setProperties
125: */
126: public Properties getProperties() {
127: return properties;
128: }
129:
130: /**
131: * Return the local member name
132: */
133: public String getLocalMemberName() {
134: return localMember.toString();
135: }
136:
137: /**
138: * Return the local member
139: */
140: public Member getLocalMember(boolean alive) {
141: if (alive && localMember != null && impl != null)
142: localMember.setMemberAliveTime(System.currentTimeMillis()
143: - impl.getServiceStartTime());
144: return localMember;
145: }
146:
147: /**
148: * Sets the local member properties for broadcasting
149: */
150: public void setLocalMemberProperties(String listenHost,
151: int listenPort) {
152: properties.setProperty("tcpListenHost", listenHost);
153: properties.setProperty("tcpListenPort", String
154: .valueOf(listenPort));
155: try {
156: if (localMember != null) {
157: localMember.setHostname(listenHost);
158: localMember.setPort(listenPort);
159: } else {
160: localMember = new MemberImpl(listenHost, listenPort, 0);
161: localMember.setUniqueId(UUIDGenerator.randomUUID(true));
162: localMember.setPayload(getPayload());
163: localMember.setDomain(getDomain());
164: }
165: localMember.getData(true, true);
166: } catch (IOException x) {
167: throw new IllegalArgumentException(x);
168: }
169: }
170:
171: public void setAddress(String addr) {
172: properties.setProperty("mcastAddress", addr);
173: }
174:
175: /**
176: * @deprecated use setAddress
177: * @param addr String
178: */
179: public void setMcastAddr(String addr) {
180: setAddress(addr);
181: }
182:
183: public String getAddress() {
184: return properties.getProperty("mcastAddress");
185: }
186:
187: /**
188: * @deprecated use getAddress
189: * @return String
190: */
191: public String getMcastAddr() {
192: return getAddress();
193: }
194:
195: public void setMcastBindAddress(String bindaddr) {
196: setBind(bindaddr);
197: }
198:
199: public void setBind(String bindaddr) {
200: properties.setProperty("mcastBindAddress", bindaddr);
201: }
202:
203: /**
204: * @deprecated use getBind
205: * @return String
206: */
207: public String getMcastBindAddress() {
208: return getBind();
209: }
210:
211: public String getBind() {
212: return properties.getProperty("mcastBindAddress");
213: }
214:
215: /**
216: * @deprecated use setPort
217: * @param port int
218: */
219: public void setMcastPort(int port) {
220: setPort(port);
221: }
222:
223: public void setPort(int port) {
224: properties.setProperty("mcastPort", String.valueOf(port));
225: }
226:
227: /**
228: * @deprecated use getPort()
229: * @return int
230: */
231: public int getMcastPort() {
232: return getPort();
233: }
234:
235: public int getPort() {
236: String p = properties.getProperty("mcastPort");
237: return new Integer(p).intValue();
238: }
239:
240: /**
241: * @deprecated use setFrequency
242: * @param time long
243: */
244: public void setMcastFrequency(long time) {
245: setFrequency(time);
246: }
247:
248: public void setFrequency(long time) {
249: properties.setProperty("mcastFrequency", String.valueOf(time));
250: }
251:
252: /**
253: * @deprecated use getFrequency
254: * @return long
255: */
256: public long getMcastFrequency() {
257: return getFrequency();
258: }
259:
260: public long getFrequency() {
261: String p = properties.getProperty("mcastFrequency");
262: return new Long(p).longValue();
263: }
264:
265: public void setMcastDropTime(long time) {
266: setDropTime(time);
267: }
268:
269: public void setDropTime(long time) {
270: properties.setProperty("memberDropTime", String.valueOf(time));
271: }
272:
273: /**
274: * @deprecated use getDropTime
275: * @return long
276: */
277: public long getMcastDropTime() {
278: return getDropTime();
279: }
280:
281: public long getDropTime() {
282: String p = properties.getProperty("memberDropTime");
283: return new Long(p).longValue();
284: }
285:
286: /**
287: * Check if a required property is available.
288: * @param properties The set of properties
289: * @param name The property to check for
290: */
291: protected void hasProperty(Properties properties, String name) {
292: if (properties.getProperty(name) == null)
293: throw new IllegalArgumentException(
294: "McastService:Required property \"" + name
295: + "\" is missing.");
296: }
297:
298: /**
299: * Start broadcasting and listening to membership pings
300: * @throws java.lang.Exception if a IO error occurs
301: */
302: public void start() throws java.lang.Exception {
303: start(MembershipService.MBR_RX);
304: start(MembershipService.MBR_TX);
305: }
306:
307: public void start(int level) throws java.lang.Exception {
308: hasProperty(properties, "mcastPort");
309: hasProperty(properties, "mcastAddress");
310: hasProperty(properties, "memberDropTime");
311: hasProperty(properties, "mcastFrequency");
312: hasProperty(properties, "tcpListenPort");
313: hasProperty(properties, "tcpListenHost");
314:
315: if (impl != null) {
316: impl.start(level);
317: return;
318: }
319: String host = getProperties().getProperty("tcpListenHost");
320: int port = Integer.parseInt(getProperties().getProperty(
321: "tcpListenPort"));
322:
323: if (localMember == null) {
324: localMember = new MemberImpl(host, port, 100);
325: localMember.setUniqueId(UUIDGenerator.randomUUID(true));
326: } else {
327: localMember.setHostname(host);
328: localMember.setPort(port);
329: localMember.setMemberAliveTime(100);
330: }
331: if (this .payload != null)
332: localMember.setPayload(payload);
333: if (this .domain != null)
334: localMember.setDomain(domain);
335: localMember.setServiceStartTime(System.currentTimeMillis());
336: java.net.InetAddress bind = null;
337: if (properties.getProperty("mcastBindAddress") != null) {
338: bind = java.net.InetAddress.getByName(properties
339: .getProperty("mcastBindAddress"));
340: }
341: int ttl = -1;
342: int soTimeout = -1;
343: if (properties.getProperty("mcastTTL") != null) {
344: try {
345: ttl = Integer.parseInt(properties
346: .getProperty("mcastTTL"));
347: } catch (Exception x) {
348: log.error("Unable to parse mcastTTL="
349: + properties.getProperty("mcastTTL"), x);
350: }
351: }
352: if (properties.getProperty("mcastSoTimeout") != null) {
353: try {
354: soTimeout = Integer.parseInt(properties
355: .getProperty("mcastSoTimeout"));
356: } catch (Exception x) {
357: log.error("Unable to parse mcastSoTimeout="
358: + properties.getProperty("mcastSoTimeout"), x);
359: }
360: }
361:
362: impl = new McastServiceImpl((MemberImpl) localMember, Long
363: .parseLong(properties.getProperty("mcastFrequency")),
364: Long
365: .parseLong(properties
366: .getProperty("memberDropTime")),
367: Integer.parseInt(properties.getProperty("mcastPort")),
368: bind, java.net.InetAddress.getByName(properties
369: .getProperty("mcastAddress")), ttl, soTimeout,
370: this );
371:
372: impl.start(level);
373:
374: }
375:
376: /**
377: * Stop broadcasting and listening to membership pings
378: */
379: public void stop(int svc) {
380: try {
381: if (impl != null && impl.stop(svc))
382: impl = null;
383: } catch (Exception x) {
384: log.error("Unable to stop the mcast service, level:" + svc
385: + ".", x);
386: }
387: }
388:
389: /**
390: * Return all the members by name
391: */
392: public String[] getMembersByName() {
393: Member[] currentMembers = getMembers();
394: String[] membernames;
395: if (currentMembers != null) {
396: membernames = new String[currentMembers.length];
397: for (int i = 0; i < currentMembers.length; i++) {
398: membernames[i] = currentMembers[i].toString();
399: }
400: } else
401: membernames = new String[0];
402: return membernames;
403: }
404:
405: /**
406: * Return the member by name
407: */
408: public Member findMemberByName(String name) {
409: Member[] currentMembers = getMembers();
410: for (int i = 0; i < currentMembers.length; i++) {
411: if (name.equals(currentMembers[i].toString()))
412: return currentMembers[i];
413: }
414: return null;
415: }
416:
417: /**
418: * has members?
419: */
420: public boolean hasMembers() {
421: if (impl == null || impl.membership == null)
422: return false;
423: return impl.membership.hasMembers();
424: }
425:
426: public Member getMember(Member mbr) {
427: if (impl == null || impl.membership == null)
428: return null;
429: return impl.membership.getMember(mbr);
430: }
431:
432: /**
433: * Return all the members
434: */
435: protected static final Member[] EMPTY_MEMBERS = new Member[0];
436:
437: public Member[] getMembers() {
438: if (impl == null || impl.membership == null)
439: return EMPTY_MEMBERS;
440: return impl.membership.getMembers();
441: }
442:
443: /**
444: * Add a membership listener, this version only supports one listener per service,
445: * so calling this method twice will result in only the second listener being active.
446: * @param listener The listener
447: */
448: public void setMembershipListener(MembershipListener listener) {
449: this .listener = listener;
450: }
451:
452: /**
453: * Remove the membership listener
454: */
455: public void removeMembershipListener() {
456: listener = null;
457: }
458:
459: public void memberAdded(Member member) {
460: if (listener != null)
461: listener.memberAdded(member);
462: }
463:
464: /**
465: * Callback from the impl when a new member has been received
466: * @param member The member
467: */
468: public void memberDisappeared(Member member) {
469: if (listener != null)
470: listener.memberDisappeared(member);
471: }
472:
473: /**
474: * @deprecated use getSoTimeout
475: * @return int
476: */
477: public int getMcastSoTimeout() {
478: return getSoTimeout();
479: }
480:
481: public int getSoTimeout() {
482: return mcastSoTimeout;
483: }
484:
485: /**
486: * @deprecated use setSoTimeout
487: * @param mcastSoTimeout int
488: */
489: public void setMcastSoTimeout(int mcastSoTimeout) {
490: setSoTimeout(mcastSoTimeout);
491: }
492:
493: public void setSoTimeout(int mcastSoTimeout) {
494: this .mcastSoTimeout = mcastSoTimeout;
495: properties.setProperty("mcastSoTimeout", String
496: .valueOf(mcastSoTimeout));
497: }
498:
499: /**
500: * @deprecated use getTtl
501: * @return int
502: */
503: public int getMcastTTL() {
504: return getTtl();
505: }
506:
507: public int getTtl() {
508: return mcastTTL;
509: }
510:
511: public byte[] getPayload() {
512: return payload;
513: }
514:
515: public byte[] getDomain() {
516: return domain;
517: }
518:
519: /**
520: * @deprecated use setTtl
521: * @param mcastTTL int
522: */
523: public void setMcastTTL(int mcastTTL) {
524: setTtl(mcastTTL);
525: }
526:
527: public void setTtl(int mcastTTL) {
528: this .mcastTTL = mcastTTL;
529: properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
530: }
531:
532: public void setPayload(byte[] payload) {
533: this .payload = payload;
534: if (localMember != null) {
535: localMember.setPayload(payload);
536: localMember.getData(true, true);
537: try {
538: if (impl != null)
539: impl.send(false);
540: } catch (Exception x) {
541: log.error("Unable to send payload update.", x);
542: }
543: }
544: }
545:
546: public void setDomain(byte[] domain) {
547: this .domain = domain;
548: if (localMember != null) {
549: localMember.setDomain(domain);
550: localMember.getData(true, true);
551: try {
552: if (impl != null)
553: impl.send(false);
554: } catch (Exception x) {
555: log.error("Unable to send domain update.", x);
556: }
557: }
558: }
559:
560: /**
561: * Simple test program
562: * @param args Command-line arguments
563: * @throws Exception If an error occurs
564: */
565: public static void main(String args[]) throws Exception {
566: if (log.isInfoEnabled())
567: log.info("Usage McastService hostname tcpport");
568: McastService service = new McastService();
569: java.util.Properties p = new java.util.Properties();
570: p.setProperty("mcastPort", "5555");
571: p.setProperty("mcastAddress", "224.10.10.10");
572: p.setProperty("mcastClusterDomain", "catalina");
573: p.setProperty("bindAddress", "localhost");
574: p.setProperty("memberDropTime", "3000");
575: p.setProperty("mcastFrequency", "500");
576: p.setProperty("tcpListenPort", "4000");
577: p.setProperty("tcpListenHost", "127.0.0.1");
578: service.setProperties(p);
579: service.start();
580: Thread.sleep(60 * 1000 * 60);
581: }
582: }
|