ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

J2EE Clustering with JBoss
Pages: 1, 2, 3

Maybe a picture will help. Here is a screenshot of the client MBean attributes:

HANotification Client Attributes
Figure 5. HANotification Client Attributes

The first attribute points to the name of the cluster notification broadcaster to which we will subscribe. The next attribute of interest is ReceivedNotifications. When a message is sent to any deployed instance of the broadcaster MBean on any of the partition nodes, it will be received and listed in the Value column for this attribute. In the screenshot above, there are two received notifications, iivanov2 and mau. Even though it is not obvious from the picture, one of these messages was received from a remote host.

Now, a screenshot of the MBean operation of interest:

HANotification client operation
Figure 6. HANotification client operation

The name is not very friendly, but it is distinct and will do for this example. When this operation is invoked with a text message as its argument, the value will appear in the ReceivedNotification attribute on each of the client MBeans deployed in the same partition.

Let's look at the code of the client MBean to see that it is actually straightforward. I made an attempt to annotate the code and keep it simple, so that there is no need for additional analysis.

public class HANotificationBroadcasterClientExample
  extends ServiceMBeanSupport
  implements HANotificationBroadcasterClientExampleMBean, NotificationListener
{

  /**
   * 
   * On service start, subscribes to notification sent by this broadcaster or
   * its remote peers.
   * 
   */
  protected void startService() throws Exception
  {
    super.startService();
    addHANotificationListener(this);
  }

  /**
   * 
   * On service stop, unsubscribes to notification sent by this broadcaster or
   * its remote peers.
   * 
   */
  protected void stopService() throws Exception
  {
    removeHANotificationListener(this);
    super.stopService();
  }

  /**
   * Broadcasts a notification to the cluster partition.
   * 
   * This example does not ensure that a notification sequence number 
   * is unique throughout the partition.
   * 
   */
  public void sendTextMessageViaHANBExample(String message) 
    throws InstanceNotFoundException, MBeanException, ReflectionException
  {
    long now = System.currentTimeMillis();
    Notification notification =
      new Notification(
        "hanotification.example.counter",
        super.getServiceName(),
        now,
        now,
        message);
    server.invoke(
        broadcasterName_,
        "sendNotification",
        new Object[] { notification },
        new String[] { Notification.class.getName() }
        );
  }

  /**
   * Lists the notifications received on the cluster partition
   */
  public Collection getReceivedNotifications()
  {
    return messages_;
  }

  /**
   * @return the name of the broadcaster MBean
   */
  public String getHANotificationBroadcasterName()
  {
    return broadcasterName_ == null ? null : broadcasterName_.toString();
  }

  /**
   * 
   * Sets the name of the broadcaster MBean.
   * 
   * @param 
   */
  public void setHANotificationBroadcasterName(String newBroadcasterName)
    throws InvalidParameterException
  {
    if (newBroadcasterName == null)
    {
      throw new InvalidParameterException("Broadcaster MBean must be specified");
    }
    try
    {
      broadcasterName_ = new ObjectName(newBroadcasterName);
    }
    catch (MalformedObjectNameException mone)
    {
      log.error("Broadcaster MBean Object Name is malformed", mone);
      throw new InvalidParameterException("Broadcaster MBean is not correctly formatted");
    }
  }

  protected void addHANotificationListener(NotificationListener listener) 
                                         throws InstanceNotFoundException
  {
    server.addNotificationListener(broadcasterName_, listener, 
      /* no need for filter */ null, 
      /* no handback object */ null);
  }

  protected void removeHANotificationListener(NotificationListener listener) 
                 throws InstanceNotFoundException, ListenerNotFoundException
  {
    server.removeNotificationListener(broadcasterName_, listener);
  }

  public void handleNotification(
    Notification notification,
    java.lang.Object handback)
  {
    messages_.add(notification.getMessage());
  }

  // Attributes ----------------------------------------------

  Collection messages_ = new LinkedList();

  /**
   * The broadcaster MBean that this class listens to and delegates HA notifications to
   */
  ObjectName broadcasterName_ = null;

}

The key points of the code are highlighted. Notice how subscription to the cluster broadcaster is accomplished via the MBean server API.

It is also important to note that the subscription is local. Both the client and the MBean server reside in the same VM. The sendNotification() invocation is also local to the VM. The cluster broadcaster hides the implementation details of working together with its remote peers to deliver notifications throughout all nodes.

If you aren't too concerned with dependencies on JBoss-specific classes, you can directly extend the cluster broadcaster class, in which case all method calls will be direct instead of proxied through the MBean Server. Here is the code for an extended broadcaster:

public class HANotificationBroadcasterExample
  extends HAServiceMBeanSupport
  implements HANotificationBroadcasterExampleMBean
{
  
  /**
   * 
   * On service start, subscribes to notification sent by this broadcaster or
   * its remote peers.
   * 
   */
  protected void startService() throws Exception
  {
    super.startService();
    addNotificationListener(listener_, /* no need for filter */ null, /* no handback object */ null);
  }
  
  /**
   * 
   * On service stop, unsubscribes to notification sent by this broadcaster or
   * its remote peers.
   * 
   */  
  protected void stopService() throws Exception
  {
    removeNotificationListener(listener_);
    super.stopService();
  }
  
  /**
   * Broadcasts a notification to the cluster partition.
   * 
   * This example does not ensure that a notification sequence number 
   * is unique throughout the partition.
   * 
   */
  public void sendTextMessage(String message)
  {
    long now = System.currentTimeMillis();
    Notification notification =  
      new Notification("hanotification.example.counter", super.getServiceName(), now, now, message);
    sendNotification(notification);
  }

  /**
   * Lists the notifications received on the cluster partition
   */
  public Collection getReceivedNotifications()
  {
    return messages_;
  }

  Collection messages_ = new LinkedList();
 
  NotificationListener listener_ = new NotificationListener()
  {
    public void handleNotification(Notification notification,
                                   java.lang.Object handback)
     {
       messages_.add( notification.getMessage() );
     }
  };
}

This class behaves similarly to the previous one. If you examine its MBean View in the JMX console, you will notice that it has the same ReceivedNotifications attribute. The messaging operation is called sendTextMessage().

Conclusion

This text shows more examples where clustering is both powerful and complex. Fortunately, JBoss eliminates much of the complexity, allowing developers to enjoy the benefits of high availability. Writing singleton services in a distributed environment with JBoss is almost as easy as writing single VM singletons. If you previously invested in the JBoss Scheduler for a standalone server environment, you can keep your code and make it automatically valid in a clustered environment. Finally, lightweight notifications are no longer the prerogative of standalone applications. JBoss cluster notifications are yet another step towards practical horizontal scalability.

Ivelin Ivanov is a frequent contributor to the open source community and has recently participated in projects that include Apache Cocoon, JBoss, GNU XQuery and Jakarta Commons.


Return to ONJava.com.