1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.catalina.tribes.membership;
19
20 import java.util.Properties;
21
22 import org.apache.catalina.tribes.Member;
23 import org.apache.catalina.tribes.MembershipListener;
24 import org.apache.catalina.tribes.MembershipService;
25 import org.apache.catalina.tribes.util.StringManager;
26 import org.apache.catalina.tribes.util.UUIDGenerator;
27 import java.io.IOException;
28
29 /**
30 * A <b>membership</b> implementation using simple multicast.
31 * This is the representation of a multicast membership service.
32 * This class is responsible for maintaining a list of active cluster nodes in the cluster.
33 * If a node fails to send out a heartbeat, the node will be dismissed.
34 *
35 * @author Filip Hanik
36 * @version $Revision: 567472 $, $Date: 2007-08-19 23:12:31 +0200 (dim., 19 août 2007) $
37 */
38
39
40 public class McastService implements MembershipService,MembershipListener {
41
42 private static org.apache.juli.logging.Log log =
43 org.apache.juli.logging.LogFactory.getLog( McastService.class );
44
45 /**
46 * The string manager for this package.
47 */
48 protected StringManager sm = StringManager.getManager(Constants.Package);
49
50 /**
51 * The descriptive information about this implementation.
52 */
53 private static final String info = "McastService/2.1";
54
55 /**
56 * The implementation specific properties
57 */
58 protected Properties properties = new Properties();
59 /**
60 * A handle to the actual low level implementation
61 */
62 protected McastServiceImpl impl;
63 /**
64 * A membership listener delegate (should be the cluster :)
65 */
66 protected MembershipListener listener;
67 /**
68 * The local member
69 */
70 protected MemberImpl localMember ;
71 private int mcastSoTimeout;
72 private int mcastTTL;
73
74 protected byte[] payload;
75
76 protected byte[] domain;
77
78 /**
79 * Create a membership service.
80 */
81 public McastService() {
82 //default values
83 properties.setProperty("mcastPort","45564");
84 properties.setProperty("mcastAddress","228.0.0.4");
85 properties.setProperty("memberDropTime","3000");
86 properties.setProperty("mcastFrequency","500");
87
88 }
89
90 /**
91 * Return descriptive information about this implementation and the
92 * corresponding version number, in the format
93 * <code><description>/<version></code>.
94 */
95 public String getInfo() {
96 return (info);
97 }
98
99 /**
100 *
101 * @param properties
102 * <BR/>All are required<BR />
103 * 1. mcastPort - the port to listen to<BR>
104 * 2. mcastAddress - the mcast group address<BR>
105 * 4. bindAddress - the bind address if any - only one that can be null<BR>
106 * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
107 * 6. mcastFrequency - the frequency of sending messages<BR>
108 * 7. tcpListenPort - the port this member listens to<BR>
109 * 8. tcpListenHost - the bind address of this member<BR>
110 * @exception java.lang.IllegalArgumentException if a property is missing.
111 */
112 public void setProperties(Properties properties) {
113 hasProperty(properties,"mcastPort");
114 hasProperty(properties,"mcastAddress");
115 hasProperty(properties,"memberDropTime");
116 hasProperty(properties,"mcastFrequency");
117 hasProperty(properties,"tcpListenPort");
118 hasProperty(properties,"tcpListenHost");
119 this.properties = properties;
120 }
121
122 /**
123 * Return the properties, see setProperties
124 */
125 public Properties getProperties() {
126 return properties;
127 }
128
129 /**
130 * Return the local member name
131 */
132 public String getLocalMemberName() {
133 return localMember.toString() ;
134 }
135
136 /**
137 * Return the local member
138 */
139 public Member getLocalMember(boolean alive) {
140 if ( alive && localMember != null && impl != null) localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
141 return localMember;
142 }
143
144 /**
145 * Sets the local member properties for broadcasting
146 */
147 public void setLocalMemberProperties(String listenHost, int listenPort) {
148 properties.setProperty("tcpListenHost",listenHost);
149 properties.setProperty("tcpListenPort",String.valueOf(listenPort));
150 try {
151 if (localMember != null) {
152 localMember.setHostname(listenHost);
153 localMember.setPort(listenPort);
154 } else {
155 localMember = new MemberImpl(listenHost, listenPort, 0);
156 localMember.setUniqueId(UUIDGenerator.randomUUID(true));
157 localMember.setPayload(getPayload());
158 localMember.setDomain(getDomain());
159 }
160 localMember.getData(true, true);
161 }catch ( IOException x ) {
162 throw new IllegalArgumentException(x);
163 }
164 }
165
166 public void setAddress(String addr) {
167 properties.setProperty("mcastAddress", addr);
168 }
169
170 /**
171 * @deprecated use setAddress
172 * @param addr String
173 */
174 public void setMcastAddr(String addr) {
175 setAddress(addr);
176 }
177
178 public String getAddress() {
179 return properties.getProperty("mcastAddress");
180 }
181
182 /**
183 * @deprecated use getAddress
184 * @return String
185 */
186 public String getMcastAddr() {
187 return getAddress();
188 }
189
190 public void setMcastBindAddress(String bindaddr) {
191 setBind(bindaddr);
192 }
193
194 public void setBind(String bindaddr) {
195 properties.setProperty("mcastBindAddress", bindaddr);
196 }
197 /**
198 * @deprecated use getBind
199 * @return String
200 */
201 public String getMcastBindAddress() {
202 return getBind();
203 }
204
205 public String getBind() {
206 return properties.getProperty("mcastBindAddress");
207 }
208
209 /**
210 * @deprecated use setPort
211 * @param port int
212 */
213 public void setMcastPort(int port) {
214 setPort(port);
215 }
216
217 public void setPort(int port) {
218 properties.setProperty("mcastPort", String.valueOf(port));
219 }
220
221 public void setRecoveryCounter(int recoveryCounter) {
222 properties.setProperty("recoveryCounter", String.valueOf(recoveryCounter));
223 }
224
225 public void setRecoveryEnabled(boolean recoveryEnabled) {
226 properties.setProperty("recoveryEnabled", String.valueOf(recoveryEnabled));
227 }
228
229 public void setRecoverySleepTime(long recoverySleepTime) {
230 properties.setProperty("recoverySleepTime", String.valueOf(recoverySleepTime));
231 }
232
233
234 /**
235 * @deprecated use getPort()
236 * @return int
237 */
238 public int getMcastPort() {
239 return getPort();
240 }
241 public int getPort() {
242 String p = properties.getProperty("mcastPort");
243 return new Integer(p).intValue();
244 }
245
246 /**
247 * @deprecated use setFrequency
248 * @param time long
249 */
250 public void setMcastFrequency(long time) {
251 setFrequency(time);
252 }
253
254 public void setFrequency(long time) {
255 properties.setProperty("mcastFrequency", String.valueOf(time));
256 }
257
258 /**
259 * @deprecated use getFrequency
260 * @return long
261 */
262 public long getMcastFrequency() {
263 return getFrequency();
264 }
265
266 public long getFrequency() {
267 String p = properties.getProperty("mcastFrequency");
268 return new Long(p).longValue();
269 }
270
271 public void setMcastDropTime(long time) {
272 setDropTime(time);
273 }
274 public void setDropTime(long time) {
275 properties.setProperty("memberDropTime", String.valueOf(time));
276 }
277
278 /**
279 * @deprecated use getDropTime
280 * @return long
281 */
282 public long getMcastDropTime() {
283 return getDropTime();
284 }
285
286 public long getDropTime() {
287 String p = properties.getProperty("memberDropTime");
288 return new Long(p).longValue();
289 }
290
291 /**
292 * Check if a required property is available.
293 * @param properties The set of properties
294 * @param name The property to check for
295 */
296 protected void hasProperty(Properties properties, String name){
297 if ( properties.getProperty(name)==null) throw new IllegalArgumentException("McastService:Required property \""+name+"\" is missing.");
298 }
299
300 /**
301 * Start broadcasting and listening to membership pings
302 * @throws java.lang.Exception if a IO error occurs
303 */
304 public void start() throws java.lang.Exception {
305 start(MembershipService.MBR_RX);
306 start(MembershipService.MBR_TX);
307 }
308
309 public void start(int level) throws java.lang.Exception {
310 hasProperty(properties,"mcastPort");
311 hasProperty(properties,"mcastAddress");
312 hasProperty(properties,"memberDropTime");
313 hasProperty(properties,"mcastFrequency");
314 hasProperty(properties,"tcpListenPort");
315 hasProperty(properties,"tcpListenHost");
316
317 if ( impl != null ) {
318 impl.start(level);
319 return;
320 }
321 String host = getProperties().getProperty("tcpListenHost");
322 int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));
323
324 if ( localMember == null ) {
325 localMember = new MemberImpl(host, port, 100);
326 localMember.setUniqueId(UUIDGenerator.randomUUID(true));
327 } else {
328 localMember.setHostname(host);
329 localMember.setPort(port);
330 localMember.setMemberAliveTime(100);
331 }
332 if ( this.payload != null ) localMember.setPayload(payload);
333 if ( this.domain != null ) localMember.setDomain(domain);
334 localMember.setServiceStartTime(System.currentTimeMillis());
335 java.net.InetAddress bind = null;
336 if ( properties.getProperty("mcastBindAddress")!= null ) {
337 bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
338 }
339 int ttl = -1;
340 int soTimeout = -1;
341 if ( properties.getProperty("mcastTTL") != null ) {
342 try {
343 ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
344 } catch ( Exception x ) {
345 log.error("Unable to parse mcastTTL="+properties.getProperty("mcastTTL"),x);
346 }
347 }
348 if ( properties.getProperty("mcastSoTimeout") != null ) {
349 try {
350 soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));
351 } catch ( Exception x ) {
352 log.error("Unable to parse mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);
353 }
354 }
355
356 impl = new McastServiceImpl((MemberImpl)localMember,Long.parseLong(properties.getProperty("mcastFrequency")),
357 Long.parseLong(properties.getProperty("memberDropTime")),
358 Integer.parseInt(properties.getProperty("mcastPort")),
359 bind,
360 java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
361 ttl,
362 soTimeout,
363 this);
364 String value = properties.getProperty("recoveryEnabled","true");
365 boolean recEnabled = Boolean.valueOf(value).booleanValue() ;
366 impl.setRecoveryEnabled(recEnabled);
367 int recCnt = Integer.parseInt(properties.getProperty("recoveryCounter","10"));
368 impl.setRecoveryCounter(recCnt);
369 long recSlpTime = Long.parseLong(properties.getProperty("recoverySleepTime","5000"));
370 impl.setRecoverySleepTime(recSlpTime);
371
372
373 impl.start(level);
374
375
376 }
377
378
379 /**
380 * Stop broadcasting and listening to membership pings
381 */
382 public void stop(int svc) {
383 try {
384 if ( impl != null && impl.stop(svc) ) impl = null;
385 } catch ( Exception x) {
386 log.error("Unable to stop the mcast service, level:"+svc+".",x);
387 }
388 }
389
390
391 /**
392 * Return all the members by name
393 */
394 public String[] getMembersByName() {
395 Member[] currentMembers = getMembers();
396 String [] membernames ;
397 if(currentMembers != null) {
398 membernames = new String[currentMembers.length];
399 for (int i = 0; i < currentMembers.length; i++) {
400 membernames[i] = currentMembers[i].toString() ;
401 }
402 } else
403 membernames = new String[0] ;
404 return membernames ;
405 }
406
407 /**
408 * Return the member by name
409 */
410 public Member findMemberByName(String name) {
411 Member[] currentMembers = getMembers();
412 for (int i = 0; i < currentMembers.length; i++) {
413 if (name.equals(currentMembers[i].toString()))
414 return currentMembers[i];
415 }
416 return null;
417 }
418
419 /**
420 * has members?
421 */
422 public boolean hasMembers() {
423 if ( impl == null || impl.membership == null ) return false;
424 return impl.membership.hasMembers();
425 }
426
427 public Member getMember(Member mbr) {
428 if ( impl == null || impl.membership == null ) 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 public Member[] getMembers() {
437 if ( impl == null || impl.membership == null ) return EMPTY_MEMBERS;
438 return impl.membership.getMembers();
439 }
440 /**
441 * Add a membership listener, this version only supports one listener per service,
442 * so calling this method twice will result in only the second listener being active.
443 * @param listener The listener
444 */
445 public void setMembershipListener(MembershipListener listener) {
446 this.listener = listener;
447 }
448 /**
449 * Remove the membership listener
450 */
451 public void removeMembershipListener(){
452 listener = null;
453 }
454
455 public void memberAdded(Member member) {
456 if ( listener!=null ) listener.memberAdded(member);
457 }
458
459 /**
460 * Callback from the impl when a new member has been received
461 * @param member The member
462 */
463 public void memberDisappeared(Member member)
464 {
465 if ( listener!=null ) listener.memberDisappeared(member);
466 }
467
468 /**
469 * @deprecated use getSoTimeout
470 * @return int
471 */
472 public int getMcastSoTimeout() {
473 return getSoTimeout();
474 }
475
476 public int getSoTimeout() {
477 return mcastSoTimeout;
478 }
479
480 /**
481 * @deprecated use setSoTimeout
482 * @param mcastSoTimeout int
483 */
484 public void setMcastSoTimeout(int mcastSoTimeout) {
485 setSoTimeout(mcastSoTimeout);
486 }
487
488 public void setSoTimeout(int mcastSoTimeout) {
489 this.mcastSoTimeout = mcastSoTimeout;
490 properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));
491 }
492
493 /**
494 * @deprecated use getTtl
495 * @return int
496 */
497 public int getMcastTTL() {
498 return getTtl();
499 }
500
501 public int getTtl() {
502 return mcastTTL;
503 }
504
505 public byte[] getPayload() {
506 return payload;
507 }
508
509 public byte[] getDomain() {
510 return domain;
511 }
512
513 /**
514 * @deprecated use setTtl
515 * @param mcastTTL int
516 */
517 public void setMcastTTL(int mcastTTL) {
518 setTtl(mcastTTL);
519 }
520
521 public void setTtl(int mcastTTL) {
522 this.mcastTTL = mcastTTL;
523 properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
524 }
525
526 public void setPayload(byte[] payload) {
527 this.payload = payload;
528 if ( localMember != null ) {
529 localMember.setPayload(payload);
530 localMember.getData(true,true);
531 try {
532 if (impl != null) impl.send(false);
533 }catch ( Exception x ) {
534 log.error("Unable to send payload update.",x);
535 }
536 }
537 }
538
539 public void setDomain(byte[] domain) {
540 this.domain = domain;
541 if ( localMember != null ) {
542 localMember.setDomain(domain);
543 localMember.getData(true,true);
544 try {
545 if (impl != null) impl.send(false);
546 }catch ( Exception x ) {
547 log.error("Unable to send domain update.",x);
548 }
549 }
550 }
551
552 /**
553 * Simple test program
554 * @param args Command-line arguments
555 * @throws Exception If an error occurs
556 */
557 public static void main(String args[]) throws Exception {
558 if(log.isInfoEnabled())
559 log.info("Usage McastService hostname tcpport");
560 McastService service = new McastService();
561 java.util.Properties p = new java.util.Properties();
562 p.setProperty("mcastPort","5555");
563 p.setProperty("mcastAddress","224.10.10.10");
564 p.setProperty("mcastClusterDomain","catalina");
565 p.setProperty("bindAddress","localhost");
566 p.setProperty("memberDropTime","3000");
567 p.setProperty("mcastFrequency","500");
568 p.setProperty("tcpListenPort","4000");
569 p.setProperty("tcpListenHost","127.0.0.1");
570 service.setProperties(p);
571 service.start();
572 Thread.sleep(60*1000*60);
573 }
574 }