Fusion: Mimic NSNotification functionality from Obj-C into Java with Spring

The first time you heard of an NSNotification in Objective-C, you probably thought it was a mechanism that is used to notify a user of an event, but you quickly learn that it is designed to send event notifications from one part of your app to another. For example, you are using ASIHttp to load data asynchronously from a RESTful Web Service and you want to know when that data is loaded so that you can refresh a UI component. There are other ways to achieve this besides using NSNotification like a delegate or a reference to the object to call but that requires a tighter coupling that we would often like since layering and separation of the code is important for maintainability.

Let’s look at an example of sending and receiving an NSNotification

//Class 1
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CONNECTION_FAILURE object:nil];
//Class 2
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionFailure:) name:NOTIFICATION_CONNECTION_FAILURE object:nil];

//somewhere after the event is fired, maybe in viewWillDisappear delegate method
[[NSNotificationCenter defaultCenter] removeObserver:self name:NOTIFICATION_CONNECTION_FAILURE object:nil];

Pretty simple mechanism for wiring up a notification, but what if we are in Java and we want to use the same mechanism. Often the reason for doing this is Java is that you have a hierarchy of UI components in Swing, GWT or Vaadin and you have an event that takes place deep in one hierarchy that needs to be received deep in another. In this cases a listener won’t work because the objects are not related to each other and using a listener would require coupling them tightly together. Spring has just such an abstraction to assist for this purpose.

The following technique will decouple your code from itself but will add a coupling to Spring and JMX. This is an acceptable tradeoff. Let’s look at how we can publish a notification. To make this work, you need to follow the instructions for creating a Bean that you want to manage and mapping it in the mbean exporter per the Spring documentation. Also, make note that this technique is for beans that have been exported as managed beans in JMX. For a more rudimentary notification mechanism just use the raw JMX API Notification API itself.

package com.domain.notification;

import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.export.notification.NotificationPublisher;
import javax.management.Notification;

@ManagedResource(objectName="bean:name=MyTestNotification", description="My Managed Bean", log=true,
    logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate", persistPeriod=200,
    persistLocation="foo", persistName="bar"
public class MyTestNotification implements MyBean, NotificationPublisherAware {

    private String name;
    private int age;
    private boolean isSuperman;
    private NotificationPublisher publisher;

    // other getters and setters omitted for clarity

    @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
    public int add(int x, int y) {
        int answer = x + y;
        this.publisher.sendNotification(new Notification("add", this, 0));
        return answer;
    }

    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.publisher = notificationPublisher;
    }

public void setAge(int age) {
    this.age = age;
  }

  @ManagedAttribute(description="The Name Attribute",
      currencyTimeLimit=20,
      defaultValue="bar",
      persistPolicy="OnUpdate")
  public void setName(String name) {
    this.name = name;
  }

  @ManagedAttribute(defaultValue="foo", persistPeriod=300)
  public String getName() {
    return name;
  }

  @ManagedOperation(description="Add two numbers")
  @ManagedOperationParameters({
    @ManagedOperationParameter(name = "x", description = "The first number"),
    @ManagedOperationParameter(name = "y", description = "The second number")})
  public int add(int x, int y) {
    return x + y;
  }

  public void dontExposeMe() {
    throw new RuntimeException();
  }
}

In the case of this class, the notification will pop when someone calls the add() method of the MyTestNotificationBean. add() is a managed method in JMX at this point. Now lets look at how to receive this notification from another class.

package com.domain.notification;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

public class EventNotificationListener
               implements NotificationListener, NotificationFilter {

    public void handleNotification(Notification notification, Object handback) {
        System.out.println(notification); //receive the add notification
        System.out.println(handback);
    }

    public boolean isNotificationEnabled(Notification notification) {
        return AttributeChangeNotification.class.isAssignableFrom(notification.getClass());
    }

There are many ways to wire this together to make it work and this is just one example. The disadvantages of this approach is that it is nowhere near as simple as the NSNotification mechanism in Objective C and it provides some overhead since the MyTestNotification class will need to be mounted as an managed bean in JMX.

As mentioned, we can also forgo Spring and use straight JMX to achieve our goal. Let’s look at an example.

import javax.management.*;

public class NyNotification
        extends NotificationBroadcasterSupport
        implements MyBean {

public void myMethod() {

Notification n =
	    new AttributeChangeNotification(this, 10,
					    "I changed",
					    "Change",
					    "int",
					    20,
					    this.test);

	/* Now send the notification using the sendNotification method
	   inherited from the parent class NotificationBroadcasterSupport. */
	sendNotification(n);

}

   @Override
    public MBeanNotificationInfo[] getNotificationInfo() {
        String[] types = new String[]{
            AttributeChangeNotification.ATTRIBUTE_CHANGE
        };

        String name = AttributeChangeNotification.class.getName();
        String description = "An attribute of this MBean has changed";
        MBeanNotificationInfo info =
                new MBeanNotificationInfo(types, name, description);
        return new MBeanNotificationInfo[]{info};
    }

@Override
 public void handleNotification(NotificationListener listener, Notification notification, Object handback) {
        System.out.println(notification); //receive the notification
        System.out.println(handback);
    }
...

Still a lot of coupling going on here and a disadvantage is that we are extending NotificationBroadcasterSupport. While we may be creating a nice way to mimic notifications, these techniques should be used as a “last resort” mechanism.

Another mechanism that can be used for this purpose is the ApplicationContext’s publishEvent(ApplicationEvent event) method or ApplicationEventPublisher if you have no access to the an ApplicationContext. This is a nice quick way to register an event and get a notification. Let’s look at the example.

...
       //We'll publish en event somewhere
        MyEvent fileUploadEvent = new MyEvent(this, user);
        getApplicationContext().publishEvent(fileUploadEvent);

//now somewhere else we can receive it.

public class EventListener implements ApplicationListener {

	@Override
	public void onApplicationEvent(ApplicationEvent evt) {
		if (evt instanceof MyEvent)
                   //do something

...

We need to register our sync executor in the ApplicationContext.xml

<bean id=”applicationEventMulticaster” class=”org.springframework.context.event.SimpleApplicationEventMulticaster”>
<property name=”taskExecutor”>
<bean class=”org.springframework.scheduling.timer.TimerTaskExecutor”/>
</property>
</bean>

or as an alternative, we can extend SimpleApplicationEventMulticaster

public class GlobalEventMulticaster extends SimpleApplicationEventMulticaster implements InitializingBean {

private ApplicationContext application;

 public void afterPropertiesSet() throws Exception {

        if (this.application == null) {

            this.application = ///get Your application context

        }

    }

References

JMX Notifications

Spring/JMX Notifications

About these ads

About Chris Hardin
Chief Architect at Doozer Software in Birmingham, Al. I specialize in Java and .NET Architecture, Ajax Frameworks and Mobile Architecture with iOS and Android.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: