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());
}
}