KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ha > jmx > HAServiceMBeanSupport


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.ha.jmx;
23
24 import java.io.Serializable JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import javax.management.AttributeChangeNotification JavaDoc;
30 import javax.management.InstanceNotFoundException JavaDoc;
31 import javax.management.Notification JavaDoc;
32 import javax.management.ObjectInstance JavaDoc;
33 import javax.management.Query JavaDoc;
34 import javax.management.QueryExp JavaDoc;
35
36 import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
37 import org.jboss.ha.framework.interfaces.DistributedState;
38 import org.jboss.ha.framework.interfaces.HAPartition;
39 import org.jboss.ha.framework.server.ClusterPartition;
40 import org.jboss.ha.framework.server.ClusterPartitionMBean;
41 import org.jboss.mx.util.MBeanProxyExt;
42 import org.jboss.system.ServiceMBeanSupport;
43 import org.jboss.system.server.ServerConfigUtil;
44
45 /**
46  *
47  * Management Bean for an HA-Service.
48  * Provides a convenient common base for cluster symmetric MBeans.
49  *
50  * This class is also a user transparent extension
51  * of the standard NotificationBroadcasterSupport
52  * to a clustered environment.
53  * Listeners register with their local broadcaster.
54  * Invoking sendNotification() on any broadcaster,
55  * will notify all listeners in the same cluster partition.
56  * TODO: The performance can be further optimized by avoiding broadcast messages
57  * when remote listener nodes are not interested (e.g. have no local subscribers)
58  * or by iterating locally over filters or remote listeners.
59  *
60  * @author Ivelin Ivanov <ivelin@apache.org> *
61  * @version $Revision: 58572 $
62  *
63  */

64 public class HAServiceMBeanSupport
65    extends ServiceMBeanSupport
66    implements HAServiceMBean
67 {
68    // Constants -----------------------------------------------------
69

70    // Attributes ----------------------------------------------------
71
private HAPartition partition_;
72    private ClusterPartitionMBean clusterPartition;
73    private String JavaDoc partitionName = ServerConfigUtil.getDefaultPartitionName();
74
75    private DistributedReplicantManager.ReplicantListener drmListener = null;
76
77    /**
78     * DRM participation TOKEN
79     */

80    private String JavaDoc REPLICANT_TOKEN = "";
81
82    private boolean sendLocalLifecycleNotifications = true;
83    private boolean sendRemoteLifecycleNotifications = true;
84    
85    // Public --------------------------------------------------------
86

87    public HAServiceMBeanSupport()
88    {
89       // for JMX
90
}
91
92    public ClusterPartitionMBean getClusterPartition()
93    {
94       return clusterPartition;
95    }
96
97    public void setClusterPartition(ClusterPartitionMBean clusterPartition)
98    {
99       if ((getState() != STARTED) && (getState() != STARTING))
100       {
101          this.clusterPartition = clusterPartition;
102       }
103    }
104
105    public String JavaDoc getPartitionName()
106    {
107       return partitionName;
108    }
109
110    public void setPartitionName(String JavaDoc newPartitionName)
111    {
112       if ((getState() != STARTED) && (getState() != STARTING))
113       {
114          partitionName = newPartitionName;
115       }
116    }
117
118
119    // Protected ------------------------------
120

121    /**
122     *
123     *
124     * Convenience method for sharing state across a cluster partition.
125     * Delegates to the DistributedStateService
126     *
127     * @param key key for the distributed object
128     * @param value the distributed object
129     *
130     */

131    public void setDistributedState(String JavaDoc key, Serializable JavaDoc value)
132       throws Exception JavaDoc
133    {
134       DistributedState ds = getPartition().getDistributedStateService();
135       ds.set(getServiceHAName(), key, value);
136    }
137
138    /**
139     *
140     * Convenience method for sharing state across a cluster partition.
141     * Delegates to the DistributedStateService
142     *
143     * @param key key for the distributed object
144     * @return Serializable the distributed object
145     *
146     */

147    public Serializable JavaDoc getDistributedState(String JavaDoc key)
148    {
149       DistributedState ds = getPartition().getDistributedStateService();
150       return ds.get(getServiceHAName(), key);
151    }
152
153    /**
154     * <p>
155     * Implementors of this method should not
156     * code the singleton logic here.
157     * The MBean lifecycle create/start/stop are separate from
158     * the singleton logic.
159     * Singleton logic should originate in becomeMaster().
160     * </p>
161     *
162     * <p>
163     * <b>Attention</b>: Always call this method when you overwrite it in a subclass
164     * because it elects the master singleton node.
165     * </p>
166     *
167     */

168    protected void startService() throws Exception JavaDoc
169    {
170       log.debug("start HAServiceMBeanSupport");
171
172       setupPartition();
173
174       registerRPCHandler();
175
176       registerDRMListener();
177    }
178
179    /**
180     * <p>
181     * <b>Attention</b>: Always call this method when you overwrite it in a subclass
182     * </p>
183     *
184     */

185    protected void stopService() throws Exception JavaDoc
186    {
187       log.debug("stop HAServiceMBeanSupport");
188
189       unregisterDRMListener();
190
191       unregisterRPCHandler();
192    }
193
194    protected void setupPartition() throws Exception JavaDoc
195    {
196       // get handle to the cluster partition
197
if (clusterPartition == null)
198       {
199          String JavaDoc pName = getPartitionName();
200          partition_ = findHAPartitionWithName(pName);
201       }
202       else
203       {
204          partition_ = clusterPartition.getHAPartition();
205          partitionName = partition_.getPartitionName();
206       }
207    }
208
209    protected void registerRPCHandler()
210    {
211       partition_.registerRPCHandler(getServiceHAName(), this);
212    }
213
214    protected void unregisterRPCHandler()
215    {
216       partition_.unregisterRPCHandler(getServiceHAName(), this);
217    }
218
219    protected void registerDRMListener() throws Exception JavaDoc
220    {
221       DistributedReplicantManager drm =
222          this.partition_.getDistributedReplicantManager();
223
224       // register to listen to topology changes, which might cause the election of a new master
225
drmListener = new DistributedReplicantManager.ReplicantListener()
226       {
227          Object JavaDoc mutex = new Object JavaDoc();
228          
229          public void replicantsChanged(
230             String JavaDoc key,
231             List JavaDoc newReplicants,
232             int newReplicantsViewId)
233          {
234             if (key.equals(getServiceHAName()))
235             {
236                // This synchronized block was added when the internal behavior of
237
// DistributedReplicantManagerImpl was changed so that concurrent
238
// replicantsChanged notifications are possible. Synchronization
239
// ensures that this change won't break non-thread-safe
240
// subclasses of HAServiceMBeanSupport.
241
synchronized(mutex)
242                {
243                   // change in the topology callback
244
HAServiceMBeanSupport.this.partitionTopologyChanged(newReplicants, newReplicantsViewId);
245                }
246             }
247          }
248       };
249       drm.registerListener(getServiceHAName(), drmListener);
250
251       // this ensures that the DRM knows that this node has the MBean deployed
252
drm.add(getServiceHAName(), REPLICANT_TOKEN);
253    }
254
255    protected void unregisterDRMListener() throws Exception JavaDoc
256    {
257       DistributedReplicantManager drm =
258          this.partition_.getDistributedReplicantManager();
259
260       // remove replicant node
261
drm.remove(getServiceHAName());
262
263       // unregister
264
drm.unregisterListener(getServiceHAName(), drmListener);
265    }
266
267    public void partitionTopologyChanged(List JavaDoc newReplicants, int newReplicantsViewId)
268    {
269       if (log.isDebugEnabled())
270       {
271          log.debug("partitionTopologyChanged(). cluster view id: " + newReplicantsViewId);
272       }
273    }
274
275    protected boolean isDRMMasterReplica()
276    {
277       DistributedReplicantManager drm =
278          getPartition().getDistributedReplicantManager();
279
280       return drm.isMasterReplica(getServiceHAName());
281    }
282
283
284    public HAPartition getPartition()
285    {
286       return partition_;
287    }
288
289     /**
290      *
291      * @param methodName
292      * @param args
293      * @throws Exception
294      * @deprecated Use {@link #callMethodOnPartition(String, Object[], Class[])} instead
295      */

296    public void callMethodOnPartition(String JavaDoc methodName, Object JavaDoc[] args)
297       throws Exception JavaDoc
298    {
299       getPartition().callMethodOnCluster(
300          getServiceHAName(),
301          methodName,
302          args,
303          true);
304    }
305
306     public void callMethodOnPartition(String JavaDoc methodName, Object JavaDoc[] args, Class JavaDoc[] types)
307       throws Exception JavaDoc
308    {
309       getPartition().callMethodOnCluster(
310          getServiceHAName(),
311          methodName,
312          args, types,
313          true);
314    }
315
316    /**
317     * Gets whether JMX Notifications should be sent to local (same JVM) listeners
318     * if the notification is for an attribute change to attribute "State".
319     * <p>
320     * Default is <code>true</code>.
321     * </p>
322     * @see #sendNotification(Notification)
323     */

324    public boolean getSendLocalLifecycleNotifications()
325    {
326       return sendLocalLifecycleNotifications;
327    }
328
329    /**
330     * Sets whether JMX Notifications should be sent to local (same JVM) listeners
331     * if the notification is for an attribute change to attribute "State".
332     * <p>
333     * Default is <code>true</code>.
334     * </p>
335     * @see #sendNotification(Notification)
336     */

337    public void setSendLocalLifecycleNotifications(boolean sendLocalLifecycleNotifications)
338    {
339       this.sendLocalLifecycleNotifications = sendLocalLifecycleNotifications;
340    }
341
342    /**
343     * Gets whether JMX Notifications should be sent to remote listeners
344     * if the notification is for an attribute change to attribute "State".
345     * <p>
346     * Default is <code>true</code>.
347     * </p>
348     * <p>
349     * See http://jira.jboss.com/jira/browse/JBAS-3194 for an example of a
350     * use case where this property should be set to false.
351     * </p>
352     *
353     * @see #sendNotification(Notification)
354     */

355    public boolean getSendRemoteLifecycleNotifications()
356    {
357       return sendRemoteLifecycleNotifications;
358    }
359
360    /**
361     * Sets whether JMX Notifications should be sent to remote listeners
362     * if the notification is for an attribute change to attribute "State".
363     * <p>
364     * Default is <code>true</code>.
365     * </p>
366     * <p>
367     * See http://jira.jboss.com/jira/browse/JBAS-3194 for an example of a
368     * use case where this property should be set to false.
369     * </p>
370     *
371     * @see #sendNotification(Notification)
372     */

373    public void setSendRemoteLifecycleNotifications(boolean sendRemoteLifecycleNotifications)
374    {
375       this.sendRemoteLifecycleNotifications = sendRemoteLifecycleNotifications;
376    }
377
378    /**
379     * Broadcast the notification to the remote listener nodes (if any) and then
380     * invoke super.sendNotification() to notify local listeners.
381     *
382     * @param notification sent out to local listeners and other nodes. It should be serializable.
383     * It is recommended that the source of the notification is an ObjectName of an MBean that
384     * is is available on all nodes where the broadcaster MBean is registered.
385     *
386     * @see #getSendLocalLifecycleNotifications()
387     * @see #getSendRemoteLifecycleNotifications()
388     * @see javax.management.NotificationBroadcasterSupport#sendNotification(Notification)
389     */

390    public void sendNotification(Notification JavaDoc notification)
391    {
392       boolean stateChange = isStateChangeNotification(notification);
393       
394       if (!stateChange || sendRemoteLifecycleNotifications)
395       {
396          try
397          {
398             // Overriding the source MBean with its ObjectName
399
// to ensure that it can be safely transferred over the wire
400
notification.setSource(this.getServiceName());
401             sendNotificationRemote(notification);
402          }
403    
404          catch (Throwable JavaDoc th)
405          {
406             boolean debug = log.isDebugEnabled();
407             if (debug)
408                log.debug("sendNotificationRemote( " + notification + " ) failed ", th);
409             // even if broadcast failed, local notification should still be sent
410

411          }
412       }
413       
414       if (!stateChange || sendLocalLifecycleNotifications)
415       {
416          sendNotificationToLocalListeners(notification);
417       }
418    }
419    
420    private boolean isStateChangeNotification(Notification JavaDoc notification)
421    {
422       boolean stateChange = false;
423       if (notification instanceof AttributeChangeNotification JavaDoc)
424       {
425          stateChange = "State".equals(((AttributeChangeNotification JavaDoc) notification).getAttributeName());
426       }
427       return stateChange;
428    }
429
430    protected void sendNotificationToLocalListeners(Notification JavaDoc notification)
431    {
432       super.sendNotification(notification);
433    }
434
435    protected void callAsyncMethodOnPartition(String JavaDoc methodName, Object JavaDoc[] args, Class JavaDoc[] types)
436       throws Exception JavaDoc
437    {
438       HAPartition partition = getPartition();
439       if (partition != null)
440       {
441          partition.callAsynchMethodOnCluster(
442             getServiceHAName(),
443             methodName,
444             args, types,
445             true);
446       }
447    }
448
449
450    /**
451     *
452     * Broadcast a notifcation remotely to the partition participants
453     *
454     * @param notification
455     */

456    protected void sendNotificationRemote(Notification JavaDoc notification)
457       throws Exception JavaDoc
458    {
459       callAsyncMethodOnPartition("_receiveRemoteNotification",
460                                  new Object JavaDoc[]{notification}, new Class JavaDoc[]{Notification JavaDoc.class});
461    }
462
463    /**
464     *
465     * Invoked by remote broadcasters.
466     * Delegates to the super class
467     *
468     */

469    public void _receiveRemoteNotification(Notification JavaDoc notification)
470    {
471       super.sendNotification(notification);
472    }
473
474
475    /**
476     *
477     * Override this method only if you need to provide a custom partition wide unique service name.
478     * The default implementation will usually work, provided that
479     * the getServiceName() method returns a unique canonical MBean name.
480     *
481     * @return partition wide unique service name
482     */

483    public String JavaDoc getServiceHAName()
484    {
485       return getServiceName().getCanonicalName();
486    }
487
488
489    protected HAPartition findHAPartitionWithName(String JavaDoc name) throws Exception JavaDoc
490    {
491       log.debug("findHAPartitionWithName, name="+name);
492       HAPartition result = null;
493       // Class name match does not work with the AOP proxy :(
494
// QueryExp classEQ = Query.eq(Query.classattr(),
495
// Query.value(ClusterPartition.class.getName()));
496
QueryExp JavaDoc matchName = Query.match(Query.attr("Name"),
497             Query.value("ClusterPartition"));
498       QueryExp JavaDoc matchPartitionName = Query.match(Query.attr("PartitionName"),
499          Query.value(name));
500       QueryExp JavaDoc exp = Query.and(matchName, matchPartitionName);
501       Set JavaDoc mbeans = this.getServer().queryMBeans(null, exp);
502       if (mbeans != null && mbeans.size() > 0)
503       {
504          for (Iterator JavaDoc iter = mbeans.iterator(); iter.hasNext();)
505          {
506             ObjectInstance JavaDoc inst = (ObjectInstance JavaDoc) iter.next();
507             try
508             {
509                ClusterPartitionMBean cp =
510                   (ClusterPartitionMBean) MBeanProxyExt.create(
511                      ClusterPartitionMBean.class,
512                      inst.getObjectName(),
513                      this.getServer());
514                result = cp.getHAPartition();
515                break;
516             }
517             catch (Exception JavaDoc e) {}
518          }
519       }
520
521       if( result == null )
522       {
523          String JavaDoc msg = "Failed to find HAPartition with PartitionName="+name;
524          throw new InstanceNotFoundException JavaDoc(msg);
525       }
526       return result;
527    }
528
529    // Private -------------------------------------------------------
530

531    // Inner classes -------------------------------------------------
532

533 }
534
535
Popular Tags