1 /*
2 * Copyright 1999,2004-2005 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.catalina.cluster.mcast;
18
19 import java.util.Properties;
20
21 import javax.management.MBeanServer;
22 import javax.management.ObjectName;
23
24 import org.apache.catalina.Cluster;
25 import org.apache.catalina.Container;
26 import org.apache.catalina.cluster.Member;
27 import org.apache.catalina.cluster.MembershipListener;
28 import org.apache.catalina.cluster.MembershipService;
29 import org.apache.catalina.cluster.tcp.SimpleTcpCluster;
30 import org.apache.catalina.core.StandardHost;
31 import org.apache.catalina.util.StringManager;
32 import org.apache.commons.modeler.Registry;
33
34 /**
35 * A <b>membership</b> implementation using simple multicast.
36 * This is the representation of a multicast membership service.
37 * This class is responsible for maintaining a list of active cluster nodes in the cluster.
38 * If a node fails to send out a heartbeat, the node will be dismissed.
39 *
40 * FIXME i18n messages
41 *
42 * @author Peter Rossbach
43 * @author Filip Hanik
44 * @version $Revision: 304032 $, $Date: 2005-07-27 11:11:55 -0400 (Wed, 27 Jul 2005) $
45 */
46
47
48 public class McastService implements MembershipService,MembershipListener {
49
50 private static org.apache.commons.logging.Log log =
51 org.apache.commons.logging.LogFactory.getLog( McastService.class );
52
53 /**
54 * The string manager for this package.
55 */
56 protected StringManager sm = StringManager.getManager(Constants.Package);
57
58 /**
59 * The descriptive information about this implementation.
60 */
61 private static final String info = "McastService/2.0";
62
63 /**
64 * The implementation specific properties
65 */
66 protected Properties properties = new Properties();
67 /**
68 * A handle to the actual low level implementation
69 */
70 protected McastServiceImpl impl;
71 /**
72 * A membership listener delegate (should be the cluster :)
73 */
74 protected MembershipListener listener;
75 /**
76 * The local member
77 */
78 protected McastMember localMember ;
79 private int mcastSoTimeout;
80 private int mcastTTL;
81
82 /**
83 * my cluster
84 */
85 private SimpleTcpCluster cluster;
86
87 /**
88 * Transmitter Mbean name
89 */
90 private ObjectName objectName;
91
92 private Registry registry;
93
94 /**
95 * Create a membership service.
96 */
97 public McastService() {
98 properties.setProperty("mcastClusterDomain", "catalina");
99 }
100
101 /**
102 * Return descriptive information about this implementation and the
103 * corresponding version number, in the format
104 * <code><description>/<version></code>.
105 */
106 public String getInfo() {
107 return (info);
108 }
109
110 /**
111 * Transmitter ObjectName
112 *
113 * @param name
114 */
115 public void setObjectName(ObjectName name) {
116 objectName = name;
117 }
118
119 public ObjectName getObjectName() {
120 return objectName;
121 }
122
123 /**
124 *
125 * @param properties
126 * <BR/>All are required<BR />
127 * 1. mcastPort - the port to listen to<BR>
128 * 2. mcastAddress - the mcast group address<BR>
129 * 3. mcastClusterDomain - the mcast cluster domain<BR>
130 * 4. bindAddress - the bind address if any - only one that can be null<BR>
131 * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
132 * 6. msgFrequency - the frequency of sending messages<BR>
133 * 7. tcpListenPort - the port this member listens to<BR>
134 * 8. tcpListenHost - the bind address of this member<BR>
135 * @exception java.lang.IllegalArgumentException if a property is missing.
136 */
137 public void setProperties(Properties properties) {
138 hasProperty(properties,"mcastPort");
139 hasProperty(properties,"mcastAddress");
140 hasProperty(properties,"mcastClusterDomain");
141 hasProperty(properties,"memberDropTime");
142 hasProperty(properties,"msgFrequency");
143 hasProperty(properties,"tcpListenPort");
144 hasProperty(properties,"tcpListenHost");
145 this.properties = properties;
146 }
147
148 /**
149 * Return the properties, see setProperties
150 */
151 public Properties getProperties() {
152 return properties;
153 }
154
155 /*
156 * configured in cluster
157 *
158 * @see org.apache.catalina.cluster.ClusterSender#setCatalinaCluster(org.apache.catalina.cluster.tcp.SimpleTcpCluster)
159 */
160 public void setCatalinaCluster(SimpleTcpCluster cluster) {
161 this.cluster = cluster;
162
163 }
164
165 /**
166 * Return the cluster for this membership service
167 */
168 public Cluster getCatalinaCluster() {
169 return cluster ;
170 }
171
172 /**
173 * Return the local member name
174 */
175 public String getLocalMemberName() {
176 return localMember.toString() ;
177 }
178
179 /**
180 * Return the local member
181 */
182 public Member getLocalMember() {
183 localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
184 return localMember;
185 }
186
187 /**
188 * Sets the local member properties for broadcasting
189 */
190 public void setLocalMemberProperties(String listenHost, int listenPort) {
191 properties.setProperty("tcpListenHost",listenHost);
192 properties.setProperty("tcpListenPort",String.valueOf(listenPort));
193 }
194
195 public void setMcastAddr(String addr) {
196 properties.setProperty("mcastAddress", addr);
197 }
198
199 public String getMcastAddr() {
200 return properties.getProperty("mcastAddress");
201 }
202
203 public void setMcastBindAddress(String bindaddr) {
204 properties.setProperty("mcastBindAddress", bindaddr);
205 }
206
207 public String getMcastBindAddress() {
208 return properties.getProperty("mcastBindAddress");
209 }
210
211 public void setMcastClusterDomain(String clusterDomain) {
212 properties.setProperty("mcastClusterDomain", clusterDomain);
213 }
214
215 public String getMcastClusterDomain() {
216 return properties.getProperty("mcastClusterDomain");
217 }
218
219 public void setMcastPort(int port) {
220 properties.setProperty("mcastPort", String.valueOf(port));
221 }
222
223 public int getMcastPort() {
224 String p = properties.getProperty("mcastPort");
225 return new Integer(p).intValue();
226 }
227
228 public void setMcastFrequency(long time) {
229 properties.setProperty("msgFrequency", String.valueOf(time));
230 }
231
232 public long getMcastFrequency() {
233 String p = properties.getProperty("msgFrequency");
234 return new Long(p).longValue();
235 }
236
237 public void setMcastDropTime(long time) {
238 properties.setProperty("memberDropTime", String.valueOf(time));
239 }
240
241 public long getMcastDropTime() {
242 String p = properties.getProperty("memberDropTime");
243 return new Long(p).longValue();
244 }
245
246 /**
247 * Check if a required property is available.
248 * @param properties The set of properties
249 * @param name The property to check for
250 */
251 protected void hasProperty(Properties properties, String name){
252 if ( properties.getProperty(name)==null) throw new IllegalArgumentException("Required property \""+name+"\" is missing.");
253 }
254
255 /**
256 * Start broadcasting and listening to membership pings
257 * @throws java.lang.Exception if a IO error occurs
258 */
259 public void start() throws java.lang.Exception {
260 start(1);
261 start(2);
262 registerMBean();
263 }
264
265 public void start(int level) throws java.lang.Exception {
266 if ( impl != null ) {
267 impl.start(level);
268 return;
269 }
270 String host = getProperties().getProperty("tcpListenHost");
271 String domain = getProperties().getProperty("mcastClusterDomain");
272 int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));
273 String name = "tcp://"+host+":"+port;
274 if ( localMember == null ) {
275 localMember = new McastMember(name, domain, host, port, 100);
276 } else {
277 localMember.setName(name);
278 localMember.setDomain(domain);
279 localMember.setHost(host);
280 localMember.setPort(port);
281 localMember.setMemberAliveTime(100);
282 }
283 java.net.InetAddress bind = null;
284 if ( properties.getProperty("mcastBindAddress")!= null ) {
285 bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
286 }
287 int ttl = -1;
288 int soTimeout = -1;
289 if ( properties.getProperty("mcastTTL") != null ) {
290 try {
291 ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
292 } catch ( Exception x ) {
293 log.error("Unable to parse mcastTTL="+properties.getProperty("mcastTTL"),x);
294 }
295 }
296 if ( properties.getProperty("mcastSoTimeout") != null ) {
297 try {
298 soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));
299 } catch ( Exception x ) {
300 log.error("Unable to parse mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);
301 }
302 }
303
304 impl = new McastServiceImpl((McastMember)localMember,Long.parseLong(properties.getProperty("msgFrequency")),
305 Long.parseLong(properties.getProperty("memberDropTime")),
306 Integer.parseInt(properties.getProperty("mcastPort")),
307 bind,
308 java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
309 ttl,
310 soTimeout,
311 this);
312
313 impl.start(level);
314 long memberwait = (Long.parseLong(properties.getProperty("msgFrequency"))*4);
315 if(log.isInfoEnabled())
316 log.info("Sleeping for "+memberwait+" secs to establish cluster membership");
317 Thread.sleep(memberwait);
318
319 }
320
321
322 /**
323 * Stop broadcasting and listening to membership pings
324 */
325 public void stop() {
326 try {
327 if ( impl != null) impl.stop();
328 } catch ( Exception x) {
329 log.error("Unable to stop the mcast service.",x);
330 }
331 impl = null;
332 unregisterMBean();
333 }
334
335 /**
336 * register mbean descriptor for package mcast
337 * @throws Exception
338 */
339 protected void initMBeans() throws Exception {
340 if(registry == null) {
341 registry = Registry.getRegistry(null, null);
342 registry.loadMetadata(this.getClass().getResourceAsStream(
343 "mbeans-descriptors.xml"));
344 }
345 }
346
347 /**
348 * register MBeans for Membership
349 *
350 */
351 protected void registerMBean() {
352 if (cluster != null) {
353 ObjectName clusterName = cluster.getObjectName();
354 try {
355 MBeanServer mserver = cluster.getMBeanServer();
356 initMBeans();
357 Container container = cluster.getContainer();
358 String name = clusterName.getDomain() + ":type=ClusterMembership";
359 if (container instanceof StandardHost) {
360 name += ",host=" + clusterName.getKeyProperty("host");
361 }
362 ObjectName mcastName = new ObjectName(name);
363 if (mserver.isRegistered(mcastName)) {
364 if (log.isWarnEnabled())
365 log.warn(sm.getString(
366 "cluster.mbean.register.allready", mcastName));
367 return;
368 }
369 setObjectName(mcastName);
370 mserver.registerMBean(cluster.getManagedBean(this),
371 getObjectName());
372 if (log.isInfoEnabled())
373 log.info("membership mbean registered (" + mcastName + ")");
374 } catch (Exception e) {
375 log.warn("membership mbean creation failed (" + clusterName + ")" ,e);
376 }
377 }
378 }
379
380 /**
381 * unregister MBeans for Membership
382 *
383 */
384 protected void unregisterMBean() {
385 if (cluster != null && getObjectName() != null) {
386 try {
387 MBeanServer mserver = cluster.getMBeanServer();
388 mserver.unregisterMBean(getObjectName());
389 } catch (Exception e) {
390 log.error(e);
391 }
392 }
393 }
394
395 /**
396 * Return all the members by name
397 */
398 public String[] getMembersByName() {
399 Member[] currentMembers = getMembers();
400 String [] membernames ;
401 if(currentMembers != null) {
402 membernames = new String[currentMembers.length];
403 for (int i = 0; i < currentMembers.length; i++) {
404 membernames[i] = currentMembers[i].toString() ;
405 }
406 } else
407 membernames = new String[0] ;
408 return membernames ;
409 }
410
411 /**
412 * Return the member by name
413 */
414 public Member findMemberByName(String name) {
415 Member[] currentMembers = getMembers();
416 for (int i = 0; i < currentMembers.length; i++) {
417 if (name.equals(currentMembers[i].toString()))
418 return currentMembers[i];
419 }
420 return null;
421 }
422
423 /**
424 * Return all the members
425 */
426 public Member[] getMembers() {
427 if ( impl == null || impl.membership == null ) return null;
428 return impl.membership.getMembers();
429 }
430 /**
431 * Add a membership listener, this version only supports one listener per service,
432 * so calling this method twice will result in only the second listener being active.
433 * @param listener The listener
434 */
435 public void addMembershipListener(MembershipListener listener) {
436 this.listener = listener;
437 }
438 /**
439 * Remove the membership listener
440 */
441 public void removeMembershipListener(){
442 listener = null;
443 }
444
445 public void memberAdded(Member member) {
446 if ( listener!=null ) listener.memberAdded(member);
447 }
448
449 /**
450 * Callback from the impl when a new member has been received
451 * @param member The member
452 */
453 public void memberDisappeared(Member member)
454 {
455 if ( listener!=null ) listener.memberDisappeared(member);
456 }
457
458 public int getMcastSoTimeout() {
459 return mcastSoTimeout;
460 }
461 public void setMcastSoTimeout(int mcastSoTimeout) {
462 this.mcastSoTimeout = mcastSoTimeout;
463 properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));
464 }
465 public int getMcastTTL() {
466 return mcastTTL;
467 }
468 public void setMcastTTL(int mcastTTL) {
469 this.mcastTTL = mcastTTL;
470 properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
471 }
472
473 /**
474 * Simple test program
475 * @param args Command-line arguments
476 * @throws Exception If an error occurs
477 */
478 public static void main(String args[]) throws Exception {
479 if(log.isInfoEnabled())
480 log.info("Usage McastService hostname tcpport");
481 McastService service = new McastService();
482 java.util.Properties p = new java.util.Properties();
483 p.setProperty("mcastPort","5555");
484 p.setProperty("mcastAddress","224.10.10.10");
485 p.setProperty("mcastClusterDomain","catalina");
486 p.setProperty("bindAddress","localhost");
487 p.setProperty("memberDropTime","3000");
488 p.setProperty("msgFrequency","500");
489 p.setProperty("tcpListenPort",args[1]);
490 p.setProperty("tcpListenHost",args[0]);
491 service.setProperties(p);
492 service.start();
493 Thread.sleep(60*1000*60);
494 }
495 }