Notifications

Some of the sSpace operations can generate notifications when they are executed. Notifications are also generated when working in clustered mode (schema) that includes a primary/backup schema. A listener can be defined to receive these notifications.

The following Space operations may trigger notifications:

  • write(), writeMultiple()

  • asyncTake(), take(), takeById(), takeByIds(), takeIfExists(), takeIfExistsById(), takeMultiple(), clear()

  • AsyncChange, change()

Space operations under transactions will trigger notifications when the transaction commits.

Notify Example

In the following example we register a listener to receive notifications when an Employee instance is written or update in the Space. The client registering the listener will receive a copy of the object written in the Space as notification. The object in the Space will continue to exist.

@EventDriven
@Notify
@NotifyType(write = true, update = true)
@TransactionalEvent
public class EmployeeListener {
    @EventTemplate
    Employee unprocessedData() {
        Employee template = new Employee();
        template.setStatus("new");
        return template;
    }

    @SpaceDataEvent
    public Employee eventListener(Employee event) {
        // process Employee
        System.out.println("Notifier Received an Employee");
        return null;
    }
}
// Register the listener
SimpleNotifyEventListenerContainer eventListener  = new SimpleNotifyContainerConfigurer(
        space).eventListenerAnnotation(new EmployeeListener())
        .notifyContainer();
eventListener.start();

//.......
eventListener.destroy();

For more information, see the Notify Container page.

Polling Example

This example works just like the notification example above, except that the object is removed from the Space:


  @EventDriven
  @Polling
  @NotifyType(write = true, update = true)
  @TransactionalEvent
  public class EmployeeListener {
        @EventTemplate
        Employee unprocessedData() {
            Employee template = new Employee();
            template.setStatus("new");
            return template;
        }

      @SpaceDataEvent
      public Employee eventListener(Empoyee event) {
        // process Employee
        System.out.println("Notifier Received an Employee");
        return null;
     }
  }
  // Register the listener
  SimplePollingEventListenerContainer pollingListener = new SimplePollingContainerConfigurer(
            space).template(new Employee())
            .eventListenerAnnotation(new Object() {
                @SpaceDataEvent
                public void eventHappened(Object event) {
                    System.out.println("onEvent called Got" + event);
                }
            }).pollingContainer();

  pollingListener.start();

  //.......
  pollingListener.destroy();

Fore more information, see the Polling Container page.

Detecting the Current Space Status

Co-Located Applications

When working in clustered mode (schema) that includes a primary/backup schema, several components within the Processing Unit need to be aware of the current Space mode and status, along with any changes that are made to it (such as event containers). A bean that is located in a stateful Processing Unit can use the SpaceStatus listener to be notified about any changes in Space mode or status. This API sends an event any time there is a change in the state of the Space.

The listener sends an event contains the following information about the SpaceMode and SuspendType.

State Description
SpaceMode
Primary The Space mode has changed to Primary.
Backup The Space mode has changed to Backup.
None The Space is currently being initialized, and is not yet in a mode.
SuspendType
None The Space is up and running normally.
Quiesced The Space is in maintenance mode. Operations are blocked until this Space becomes active within the timeout period, and operations can again be performed.
Demoting The Space was formerly in Primary mode, and is being demoted to Backup mode. Operations are blocked until the primary Space becomes active within the timeout period, and operations can again be performed.
Disconnected The Space is not available due to a connection issue.

To enable the SpaceStatus listener, add the following to the pu.xml file.

<os-core:annotation-support/>

Adding A Listener by Annotation

To add a listener by annotation, you need to create a Spring bean with a method that receives only one parameter of type SpaceStatusChangedEvent.

Example:

import com.gigaspaces.server.space.suspend.SuspendType;
import com.gigaspaces.cluster.activeelection.SpaceMode;
import org.openspaces.core.space.status.SpaceStatusChangedEvent;
import org.openspaces.core.space.status.SpaceStatusChanged;


public class MyListenerByAnnotation {

   @SpaceStatusChanged
   public void myMethod(SpaceStatusChangedEvent event) {
		// Fetch and handle suspend type from event
       SuspendType suspendType = event.getSuspendType();
       handleSuspendType(suspendType);


	// Fetch and  handle space mode from event
		spaceMode = event.getSpaceMode();
       handleSpaceMode(spaceMode);
   }

}

Adding a Listener by Interface

To add a listener by interface, you need to create a bean that implements the interface SpaceStatusChangedListenerByInterface.

Example:

import com.gigaspaces.server.space.suspend.SuspendType;
import com.gigaspaces.cluster.activeelection.SpaceMode;
import org.openspaces.core.space.status.SpaceStatusChangedEvent;
import org.openspaces.core.space.status.SpaceStatusChangedEventListener;

public class MyListenerByInterface implements SpaceStatusChangedEventListener {

   @Override
   public void onSuspendTypeChanged(SpaceStatusChangedEvent event) {
// Fetch and handle suspend type from event
       SuspendType suspendType = event.getSuspendType();
       handleSuspendType(suspendType);

// Fetch and  handle space mode from event
       SpaceMode spaceMode = event.getSpaceMode();
       handleSpaceMode(spaceMode);
   }

}

Using @PostPrimary for Primary/Backup Notifications

The SpaceStatus listener described above, which was introduced in version 14.0.1, provides expanded functionality and is the preferred method for detecting Space status changes. @PostPrimary is still supported but will be deprecated in a future version.

Using Spring support for application events, two events are defined within OpenSpaces: BeforeSpaceModeChangeEvent and AfterSpaceModeChangeEvent. Both are raised when a Space changes its mode, for example from primary to backup or vice versa, and holds the current Space mode.

Custom beans that need to be aware of the Space mode (for example, working directly against a cluster member, i.e. not using a clustered proxy of the Space, and performing operations against the Space only when it is in primary mode) can implement the Spring ApplicationListener and check for the mentioned events.

OpenSpaces also provides the Space Mode Context Loader, which can load the Spring application context when it has become primary, and unload it when it moves to backup.

In embedded mode, the space factory bean registers with the space for space mode changes. The registration is performed on the actual space instance (and not a clustered proxy of it), and any events raised are translated to the equivalent OpenSpaces space mode change events. In remote mode, a single primary event is raised.

Space mode registration can be overridden and explicitly set within the space factory configuration. Here is an example of how it can be set (it cannot register for notifications even though it is an embedded space):


<os-core:embedded-space id="space" space-name="space" register-for-space-mode-notifications="false" />

<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property name="name" value="space" />
    <property name="registerForSpaceModeNotifications" value="false" />
</bean>

EmbeddedSpaceConfigurer spaceConfigurer =
              new EmbeddedSpaceConfigurer("space").registerForSpaceModeNotifications(false);
IJSpace space = spaceConfigurer.space();

// ...

// shutting down / closing the Space
spaceConfigurer.destroy();

A bean can implement the following interfaces to get notified about Space mode changes:

Interface Implemented Method When Invoked
SpaceBeforeBackupListener void onBeforeBackup(BeforeSpaceModeChangeEvent event) Before a space becomes backup
SpaceBeforePrimaryListener void onBeforePrimary(BeforeSpaceModeChangeEvent event) Before a space becomes primary
SpaceAfterBackupListener void onAfterBackup(AfterSpaceModeChangeEvent event) After a space becomes backup
SpaceAfterPrimaryListener void onAfterPrimary(AfterSpaceModeChangeEvent event) After a space becomes primary
class MyBean implements SpaceBeforeBackupListener, SpaceAfterPrimaryListener {

    // invoked before a space becomes backup
    public void onBeforeBackup(BeforeSpaceModeChangeEvent event) {
        // Do something
    }

    // invoked after a space becomes primary
    public void onAfterPrimary(AfterSpaceModeChangeEvent event) {
        // Do something
    }

}

If the bean does not implement any of the interfaces above, another option is to annotate the bean's methods that need to be invoked when a Space mode changes.

Annotation Method Parameter When Invoked
@PreBackup none or BeforeSpaceModeChangeEvent Before a space becomes backup
@PrePrimary none or BeforeSpaceModeChangeEvent Before a space becomes primary
@PostBackup none or AfterSpaceModeChangeEvent After a space becomes backup
@PostPrimary none or AfterSpaceModeChangeEvent After a space becomes primary
class MyBean {

    // invoked before a space becomes backup; gets the BeforeSpaceModeChangeEvent as a parameter
    @PreBackup
    public void myBeforeBackupMethod(BeforeSpaceModeChangeEvent event) {
        // Do something
    }

    // invoked after a space becomes primary; doesn't get any parameter.
    @PostPrimary
    public void myAfterPrimaryMethod() {
        // Do something
    }

}

In order to enable this feature, the following should be placed within the application context configuration:


<os-core:annotation-support />

<bean id="coreAnntoationSupport" class="org.openspaces.core.config.AnnotationSupportBeanDefinitionParser" />

When there is more than one Proxy (e.g: embedded, remote, ...), the following should be done in order to be sure that the Primary/ Backup Notifications arrived from the current Space instance:

class MyBean {

    @Resource(name="gigaSpace")
    private GigaSpace gigaSpace;

    @Resource(name="gigaSpace2")
    private GigaSpace gigaSpace2;

    boolean isPrimary;

    @PostPrimary
    public void afterChangeModeToPrimary(AfterSpaceModeChangeEvent event) {
        if (SpaceUtils.isSameSpace(gigaSpace.getSpace(), event.getSpace()))
            isPrimary = true;
    }

    @PostBackup
    public void afterChangeModeToBackup(AfterSpaceModeChangeEvent event) {
        if (SpaceUtils.isSameSpace(gigaSpace.getSpace(), event.getSpace()))
            isPrimary = false;
    }

}

The method compareAndSet() allows you to compare the current value of the AtomicBoolean to an expected value. If the current value is equal to the expected value, a new value can be set on the AtomicBoolean. The compareAndSet() method is atomic, so only a single thread can execute it at the same time. Thus, the compareAndSet() method can be used to implement a simple synchronization like lock.

class MyBean {

    @Resource(name="gigaSpace")
    private GigaSpace gigaSpace;

    @Resource(name="gigaSpace2")
    private GigaSpace gigaSpace2;

    static AtomicBoolean isPostPrimaryCalled = new AtomicBoolean(false);

    @PostPrimary
    public void afterChangeModeToPrimary(AfterSpaceModeChangeEvent event) {
        if (isPostPrimaryCalled.compareAndSet(false, true)
        {
            initialize();
        }
    }

    @PostBackup
    public void afterChangeModeToBackup(AfterSpaceModeChangeEvent event) {
        if (SpaceUtils.isSameSpace(gigaSpace.getSpace(), event.getSpace()))
            isPrimary = false;
    }

}

Remote Clients

When a remote client is interested to receive events when a Space instance changing its runtime mode (from primary to backup or vice versa), it should implement the SpaceModeChangedEventListener.

For example, see the following example about registering for the event using the Administration API:

Admin admin = new AdminFactory().createAdmin();
Space space = admin.getSpaces().waitFor(spaceName, 10, TimeUnit.SECONDS);
SpaceModeChangedEventManager modeManager =  space.getSpaceModeChanged();
MySpaceModeListener spaceModeListener = new MySpaceModeListener (space);
modeManager.add(spaceModeListener);

The MySpaceModeListener should implement the SpaceModeChangedEventListener - see the following example:

public class MySpaceModeListener implements SpaceModeChangedEventListener{

    Space space ;
    public MySpaceModeListener (Space space)
    {
        this.space=space;
    }

    public void spaceModeChanged(SpaceModeChangedEvent event) {
        String partition_member = event.getSpaceInstance().getInstanceId()+"";
        if (event.getSpaceInstance().getBackupId() != 0)
        {
            partition_member = partition_member+ "_" + event.getSpaceInstance().getBackupId();
        }
    System.out.println("SpaceModeChangedEvent:  Space " + space.getName() +" - Instance " + partition_member +
            " moved into " + event.getNewMode());
    }
}