Event listeners allow Red Hat JBoss Data Grid Hot Rod servers to be able to notify remote clients of events such as CacheEntryCreated, CacheEntryModified, CacheEntryExpired and CacheEntryRemoved. Clients can choose whether or not to listen to these events to avoid flooding connected clients. This assumes that clients maintain persistent connections to the servers.
Client listeners for remote events can be added similarly to clustered listeners in library mode. The following example demonstrates a remote client listener that prints out each event it receives.
Example 8.6. Event Print Listener
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
@ClientCacheEntryCreated
public void handleCreatedEvent(ClientCacheEntryCreatedEvent e) {
System.out.println(e);
}
@ClientCacheEntryModified
public void handleModifiedEvent(ClientCacheEntryModifiedEvent e) {
System.out.println(e);
}
@ClientCacheEntryExpired
public void handleExpiredEvent(ClientCacheEntryExpiredEvent e) {
System.out.println(e);
}
@ClientCacheEntryRemoved
public void handleRemovedEvent(ClientCacheEntryRemovedEvent e) {
System.out.println(e);
}
}
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
@ClientCacheEntryCreated
public void handleCreatedEvent(ClientCacheEntryCreatedEvent e) {
System.out.println(e);
}
@ClientCacheEntryModified
public void handleModifiedEvent(ClientCacheEntryModifiedEvent e) {
System.out.println(e);
}
@ClientCacheEntryExpired
public void handleExpiredEvent(ClientCacheEntryExpiredEvent e) {
System.out.println(e);
}
@ClientCacheEntryRemoved
public void handleRemovedEvent(ClientCacheEntryRemovedEvent e) {
System.out.println(e);
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
ClientCacheEntryCreatedEvent and ClientCacheEntryModifiedEvent instances provide information on the key and version of the entry. This version can be used to invoke conditional operations on the server, such a replaceWithVersion or removeWithVersion.
ClientCacheEntryExpiredEvent events are sent when either a get() is called on an expired entry, or when the expiration reaper detects that an entry has expired. Once the entry has expired the cache will nullify the entry, and adjust its size appropriately; however, the event will only be generated in the two scenarios listed.
ClientCacheEntryRemovedEvent events are only sent when the remove operation succeeds. If a remove operation is invoked and no entry is found or there are no entries to remove, no event is generated. If users require remove events regardless of whether or not they are successful, a customized event logic can be created.
All client cache entry created, modified, and removed events provide a boolean isCommandRetried() method that will return true if the write command that caused it has to be retried due to a topology change. This indicates that the event has been duplicated or that another event was dropped and replaced, such as where a Modified event replaced a Created event.
Important
If the expected workload favors writes over reads it will be necessary to filter the events sent to prevent a large amount of excessive traffic being generated which may cause issues on either the client or the network. For more details on filtering events refer to Section 8.5.3, “Filtering Remote Events”.
Important
Remote event listeners are available for the Hot Rod Java client only.
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Non-Maven users, adjust according to your chosen build tool or download the distribution containing all JBoss Data Grid jars.
Write the client application
The following demonstrates a simple remote event listener that logs all events received.
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
@ClientCacheEntryCreated
@ClientCacheEntryModified
@ClientCacheEntryRemoved
public void handleRemoteEvent(ClientEvent event) {
System.out.println(event);
}
}
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
@ClientCacheEntryCreated
@ClientCacheEntryModified
@ClientCacheEntryRemoved
public void handleRemoteEvent(ClientEvent event) {
System.out.println(event);
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Use the remote event listener to execute operations against the remote cache
The following example demonstrates a simple main java class, which adds the remote event listener and executes some operations against the remote cache.
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The output indicates that by default, events come with the key and the internal data version associated with current value. The actual value is not sent back to the client for performance reasons. Receiving remote events has a performance impact, which is increased with cache size, as more operations are executed. To avoid inundating Hot Rod clients, filter remote events on the server side, or customize the event contents.
To prevent clients being inundated with events, Red Hat JBoss Data Grid Hot Rod remote events can be filtered by providing key/value filter factories that create instances that filter which events are sent to clients, and how these filters can act on client provided information.
Sending events to remote clients has a performance cost, which increases with the number of clients with registered remote listeners. The performance impact also increases with the number of modifications that are executed against the cache.
The performance cost can be reduced by filtering the events being sent on the server side. Custom code can be used to exclude certain events from being broadcast to the remote clients to improve performance.
Filtering can be based on either key or value information, or based on cache entry metadata. To enable filtering, a cache event filter factory that produces filter instances must be created. The following is a sample implementation that filters key “2” out of the events sent to clients.
Example 8.9. KeyValueFilter
package sample;
import java.io.Serializable;
import org.infinispan.notifications.cachelistener.filter.*;
import org.infinispan.metadata.*;
@NamedFactory(name = "basic-filter-factory")
public class BasicKeyValueFilterFactory implements CacheEventFilterFactory {
@Override public CacheEventFilter<Integer, String> getFilter(final Object[] params) {
return new BasicKeyValueFilter();
}
static class BasicKeyValueFilter implements CacheEventFilter<Integer, String>, Serializable {
@Override public boolean accept(Integer key, String oldValue, Metadata oldMetadata, String newValue, Metadata newMetadata, EventType eventType) {
return !"2".equals(key);
}
}
}
package sample;
import java.io.Serializable;
import org.infinispan.notifications.cachelistener.filter.*;
import org.infinispan.metadata.*;
@NamedFactory(name = "basic-filter-factory")
public class BasicKeyValueFilterFactory implements CacheEventFilterFactory {
@Override public CacheEventFilter<Integer, String> getFilter(final Object[] params) {
return new BasicKeyValueFilter();
}
static class BasicKeyValueFilter implements CacheEventFilter<Integer, String>, Serializable {
@Override public boolean accept(Integer key, String oldValue, Metadata oldMetadata, String newValue, Metadata newMetadata, EventType eventType) {
return !"2".equals(key);
}
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
In order to register a listener with this key value filter factory, the factory must be given a unique name, and the Hot Rod server must be plugged with the name and the cache event filter factory instance.
Custom filters can improve performance by excluding certain event information from being broadcast to the remote clients.
To plug the JBoss Data Grid Server with a custom filter use the following procedure:
Procedure 8.3. Using a Custom Filter
Create a JAR file with the filter implementation within it. Each factory must have a name assigned to it via the org.infinispan.filter.NamedFactory annotation. The example uses a KeyValueFilterFactory.
Create a META-INF/services/org.infinispan.notifications.cachelistener.filter. CacheEventFilterFactory file within the JAR file, and within it write the fully qualified class name of the filter class implementation.
Deploy the JAR file in the JBoss Data Grid Server by performing any of the following options:
Procedure 8.4. Option 1: Deploy the JAR through the deployment scanner.
Copy the JAR to the $JDG_HOME/standalone/deployments/ directory. The deployment scanner actively monitors this directory and will deploy the newly placed file.
Procedure 8.5. Option 2: Deploy the JAR through the CLI
Connect to the desired instance with the CLI:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Once connected execute the deploy command:
deploy /path/to/artifact.jar
deploy /path/to/artifact.jar
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Procedure 8.6. Option 3: Deploy the JAR as a custom module
Connect to the JDG server by running the below command:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The jar containing the Custom Filter must be defined as a module for the Server; to add this substitute the desired name of the module and the .jar name in the below command, adding additional dependencies as necessary for the Custom Filter:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
In a different window add the newly added module as a dependency to the org.infinispan module by editing $JDG_HOME/modules/system/layers/base/org/infinispan/main/module.xml. In this file add the following entry:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Restart the JDG server.
Once the server is plugged with the filter, add a remote client listener that will use the filter. The following example extends the EventLogListener implementation provided in Remote Event Client Listener Example (See Section 8.5.2, “Remote Event Client Listener Example”), and overrides the @ClientListener annotation to indicate the filter factory to use with the listener.
Example 8.10. Add Filter Factory to the Listener
@org.infinispan.client.hotrod.annotation.ClientListener(filterFactoryName = "basic-filter-factory")
public class BasicFilteredEventLogListener extends EventLogListener {}
@org.infinispan.client.hotrod.annotation.ClientListener(filterFactoryName = "basic-filter-factory")
public class BasicFilteredEventLogListener extends EventLogListener {}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The listener can now be added via the RemoteCacheAPI. The following example demonstrates this, and executes some operations against the remote cache.
Example 8.11. Register the Listener with the Server
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Important
Filter instances must be marshallable when they are deployed in a cluster in order for filtering to occur where the event is generated, even if the event is generated in a different node to where the listener is registered. To make them marshallable, either make them extend Serializable, Externalizable, or provide a custom Externalizer.
When adding client listeners, users can provide parameters to the filter factory in order to generate different filter instances with different behaviors from a single filter factory based on client-side information.
The following configuration demonstrates how to enhance the filter factory so that it can filter dynamically based on the key provided when adding the listener, rather than filtering on a statically given key.
Example 8.12. Configuring an Enhanced Filter Factory
package sample;
import java.io.Serializable;
import org.infinispan.notifications.cachelistener.filter.*;
import org.infinispan.metadata.*;
@NamedFactory(name = "basic-filter-factory")
public class BasicKeyValueFilterFactory implements CacheEventFilterFactory {
@Override public CacheEventFilter<Integer, String> getFilter(final Object[] params) {
return new BasicKeyValueFilter(params);
}
static class BasicKeyValueFilter implements CacheEventFilter<Integer, String>, Serializable {
private final Object[] params;
public BasicKeyValueFilter(Object[] params) { this.params = params; }
@Override public boolean accept(Integer key, String oldValue, Metadata oldMetadata, String newValue, Metadata newMetadata, EventType eventType) {
return !params[0].equals(key);
}
}
}
package sample;
import java.io.Serializable;
import org.infinispan.notifications.cachelistener.filter.*;
import org.infinispan.metadata.*;
@NamedFactory(name = "basic-filter-factory")
public class BasicKeyValueFilterFactory implements CacheEventFilterFactory {
@Override public CacheEventFilter<Integer, String> getFilter(final Object[] params) {
return new BasicKeyValueFilter(params);
}
static class BasicKeyValueFilter implements CacheEventFilter<Integer, String>, Serializable {
private final Object[] params;
public BasicKeyValueFilter(Object[] params) { this.params = params; }
@Override public boolean accept(Integer key, String oldValue, Metadata oldMetadata, String newValue, Metadata newMetadata, EventType eventType) {
return !params[0].equals(key);
}
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
In Red Hat JBoss Data Grid, Hot Rod remote events can be customized to contain the information required to be sent to a client. By default, events contain only a basic set of information, such as a key and type of event, in order to avoid overloading the client, and to reduce the cost of sending them.
The information included in these events can be customized to contain more information, such as values, or contain even less information. Customization is done via CacheEventConverter instances, which are created by implementing a CacheEventConverterFactory class. Each factory must have a name associated to it via the @NamedFactory annotation.
To plug the JBoss Data Grid Server with an event converter use the following procedure:
Procedure 8.7. Using a Converter
Create a JAR file with the converter implementation within it. Each factory must have a name assigned to it via the org.infinispan.filter.NamedFactory annotation.
Create a META-INF/services/org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory file within the JAR file and within it, write the fully qualified class name of the converter class implementation.
Deploy the JAR file in the JBoss Data Grid Server by performing any of the following options:
Procedure 8.8. Option 1: Deploy the JAR through the deployment scanner.
Copy the JAR to the $JDG_HOME/standalone/deployments/ directory. The deployment scanner actively monitors this directory and will deploy the newly placed file.
Procedure 8.9. Option 2: Deploy the JAR through the CLI
Connect to the desired instance with the CLI:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Once connected execute the deploy command:
deploy /path/to/artifact.jar
deploy /path/to/artifact.jar
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Procedure 8.10. Option 3: Deploy the JAR as a custom module
Connect to the JDG server by running the below command:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The jar containing the Custom Converter must be defined as a module for the Server; to add this substitute the desired name of the module and the .jar name in the below command, adding additional dependencies as necessary for the Custom Converter:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
In a different window add the newly added module as a dependency to the org.infinispan module by editing $JDG_HOME/modules/system/layers/base/org/infinispan/main/module.xml. In this file add the following entry:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Restart the JDG server.
Converters can also act on client provided information, allowing converter instances to customize events based on the information provided when the listener was added. The API allows converter parameters to be passed in when the listener is added.
When a listener is added, the name of a converter factory can be provided to use with the listener. When the listener is added, the server looks up the factory and invokes the getConverter method to get a org.infinispan.filter.Converter class instance to customize events server side.
The following example demonstrates sending custom events containing value information to remote clients for a cache of Integers and Strings. The converter generates a new custom event, which includes the value as well as the key in the event. The custom event has a bigger event payload compared with default events, however if combined with filtering, it can reduce bandwidth cost.
Example 8.14. Sending Custom Events
import org.infinispan.notifications.cachelistener.filter.*;
@NamedFactory(name = "value-added-converter-factory")
class ValueAddedConverterFactory implements CacheEventConverterFactory {
// The following types correspond to the Key, Value, and the returned Event, respectively.
public CacheEventConverter<Integer, String, ValueAddedEvent> getConverter(final Object[] params) {
return new ValueAddedConverter();
}
static class ValueAddedConverter implements CacheEventConverter<Integer, String, ValueAddedEvent> {
public ValueAddedEvent convert(Integer key, String oldValue,
Metadata oldMetadata, String newValue,
Metadata newMetadata, EventType eventType) {
return new ValueAddedEvent(key, newValue);
}
}
}
// Must be Serializable or Externalizable.
class ValueAddedEvent implements Serializable {
final Integer key;
final String value;
ValueAddedEvent(Integer key, String value) {
this.key = key;
this.value = value;
}
}
import org.infinispan.notifications.cachelistener.filter.*;
@NamedFactory(name = "value-added-converter-factory")
class ValueAddedConverterFactory implements CacheEventConverterFactory {
// The following types correspond to the Key, Value, and the returned Event, respectively.
public CacheEventConverter<Integer, String, ValueAddedEvent> getConverter(final Object[] params) {
return new ValueAddedConverter();
}
static class ValueAddedConverter implements CacheEventConverter<Integer, String, ValueAddedEvent> {
public ValueAddedEvent convert(Integer key, String oldValue,
Metadata oldMetadata, String newValue,
Metadata newMetadata, EventType eventType) {
return new ValueAddedEvent(key, newValue);
}
}
}
// Must be Serializable or Externalizable.
class ValueAddedEvent implements Serializable {
final Integer key;
final String value;
ValueAddedEvent(Integer key, String value) {
this.key = key;
this.value = value;
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Other converter implementations are able to send back events that contain no key or event type information, resulting in extremely lightweight events at the expense of having rich information provided by the event.
In order to plug the server with this converter, deploy the converter factory and associated converter class within a JAR file including a service definition inside the META-INF/services/org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory file as follows:
sample.ValueAddedConverterFactor
sample.ValueAddedConverterFactor
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The client listener must then be linked with the converter factory by adding the factory name to the @ClientListener annotation.
@ClientListener(converterFactoryName = "value-added-converter-factory")
public class CustomEventLogListener { ... }
@ClientListener(converterFactoryName = "value-added-converter-factory")
public class CustomEventLogListener { ... }
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Dynamic converter instances convert based on parameters provided when the listener is registered. Converters use the parameters received by the converter factories to enable this option. For example:
Example 8.15. Dynamic Converter
import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
class DynamicCacheEventConverterFactory implements CacheEventConverterFactory {
// The following types correspond to the Key, Value, and the returned Event, respectively.
public CacheEventConverter<Integer, String, CustomEvent> getConverter(final Object[] params) {
return new DynamicCacheEventConverter(params);
}
}
// Serializable, Externalizable or marshallable with Infinispan Externalizers needed when running in a cluster
class DynamicCacheEventConverter implements CacheEventConverter<Integer, String, CustomEvent>, Serializable {
final Object[] params;
DynamicCacheEventConverter(Object[] params) {
this.params = params;
}
public CustomEvent convert(Integer key, String oldValue, Metadata metadata, String newValue, Metadata prevMetadata, EventType eventType) {
// If the key matches a key given via parameter, only send the key information
if (params[0].equals(key))
return new ValueAddedEvent(key, null);
return new ValueAddedEvent(key, newValue);
}
}
import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
class DynamicCacheEventConverterFactory implements CacheEventConverterFactory {
// The following types correspond to the Key, Value, and the returned Event, respectively.
public CacheEventConverter<Integer, String, CustomEvent> getConverter(final Object[] params) {
return new DynamicCacheEventConverter(params);
}
}
// Serializable, Externalizable or marshallable with Infinispan Externalizers needed when running in a cluster
class DynamicCacheEventConverter implements CacheEventConverter<Integer, String, CustomEvent>, Serializable {
final Object[] params;
DynamicCacheEventConverter(Object[] params) {
this.params = params;
}
public CustomEvent convert(Integer key, String oldValue, Metadata metadata, String newValue, Metadata prevMetadata, EventType eventType) {
// If the key matches a key given via parameter, only send the key information
if (params[0].equals(key))
return new ValueAddedEvent(key, null);
return new ValueAddedEvent(key, newValue);
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The dynamic parameters required to do the conversion are provided when the listener is registered:
RemoteCache<Integer, String> cache = rcm.getCache();
cache.addClientListener(new EventLogListener(), null, new Object[]{1});
RemoteCache<Integer, String> cache = rcm.getCache();
cache.addClientListener(new EventLogListener(), null, new Object[]{1});
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Implementing a listener for custom events is slightly different to other remote events, as they involve non-default events. The same annotations are used as in other remote client listener implementations, but the callbacks receive instances of ClientCacheEntryCustomEvent<T>, where T is the type of custom event we are sending from the server. For example:
Example 8.16. Custom Event Listener Implementation
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener(converterFactoryName = "value-added-converter-factory")
public class CustomEventLogListener {
@ClientCacheEntryCreated
@ClientCacheEntryModified
@ClientCacheEntryRemoved
public void handleRemoteEvent(ClientCacheEntryCustomEvent<ValueAddedEvent> event)
{
System.out.println(event);
}
}
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener(converterFactoryName = "value-added-converter-factory")
public class CustomEventLogListener {
@ClientCacheEntryCreated
@ClientCacheEntryModified
@ClientCacheEntryRemoved
public void handleRemoteEvent(ClientCacheEntryCustomEvent<ValueAddedEvent> event)
{
System.out.println(event);
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
To use the remote event listener to execute operations against the remote cache, write a simple main Java class, which adds the remote event listener and executes some operations against the remote cache. For example:
Example 8.17. Execute Operations against the Remote Cache
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Important
Converter instances must be marshallable when they are deployed in a cluster in order for conversion to occur where the event is generated, even if the event is generated in a different node to where the listener is registered. To make them marshallable, either make them extend Serializable, Externalizable, or provide a custom Externalizer for them. Both client and server need to be aware of any custom event type and be able to marshall it in order to facilitate both server and client writing against type safe APIs. On the client side, this is done by an optional marshaller configurable via the RemoteCacheManager. On the server side, this is done by a marshaller added to the Hot Rod server configuration.
When filtering or customizing events, the KeyValueFilter and Converter instances must be marshallable. As the client listener is installed in a cluster, the filter and/or converter instances are sent to other nodes in the cluster in order for filtering and conversion to occur where the event originates, improving efficiency. These classes can be made marshallable by having them extend Serializable or by providing and registering a custom Externalizer.
To deploy a Marshaller instance server-side, use a similar method to that used for filtering and customized events.
Procedure 8.11. Deploying a Marshaller
Create a JAR file with the converter implementation within it. Each factory must have a name assigned to it via the org.infinispan.filter.NamedFactory annotation.
Create a META-INF/services/org.infinispan.commons.marshall.Marshaller file within the JAR file and within it, write the fully qualified class name of the marshaller class implementation
Deploy the JAR file in the JBoss Data Grid Server by performing any of the following options:
Procedure 8.12. Option 1: Deploy the JAR through the deployment scanner.
Copy the JAR to the $JDG_HOME/standalone/deployments/ directory. The deployment scanner actively monitors this directory and will deploy the newly placed file.
Procedure 8.13. Option 2: Deploy the JAR through the CLI
Connect to the desired instance with the CLI:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Once connected execute the deploy command:
deploy /path/to/artifact.jar
deploy /path/to/artifact.jar
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Procedure 8.14. Option 3: Deploy the JAR as a custom module
Connect to the JDG server by running the below command:
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
[$JDG_HOME] $ bin/cli.sh --connect=$IP:$PORT
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The jar containing the Custom Marshaller must be defined as a module for the Server; to add this substitute the desired name of the module and the .jar name in the below command, adding additional dependencies as necessary for the Custom Marshaller:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
In a different window add the newly added module as a dependency to the org.infinispan module by editing $JDG_HOME/modules/system/layers/base/org/infinispan/main/module.xml. In this file add the following entry:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Restart the JDG server.
The Marshaller can be deployed either in a separate jar, or in the same jar as the CacheEventConverter, and/or CacheEventFilter instances.
Note
Only the deployment of a single Marshaller instance is supported. If multiple marshaller instances are deployed, warning messages will be displayed as a reminder indicating which marshaller instance will be used.
When a client adds a remote listener, it is installed in a single node in the cluster, which is in charge of sending events back to the client for all affected operations that occur cluster-wide.
In a clustered environment, when the node containing the listener goes down, the Hot Rod client implementation transparently fails over the client listener registration to a different node. This may result in a gap in event consumption, which can be solved using one of the following solutions.
State Delivery
The @ClientListener annotation has an optional includeCurrentState parameter, which when enabled, has the server send CacheEntryCreatedEvent event instances for all existing cache entries to the client. As this behavior is driven by the client it detects when the node where the listener is registered goes offline and automatically registers the listener on another node in the cluster. By enabling includeCurrentState clients may recompute their state or computation in the event the Hot Rod client transparently fails over registered listeners. The performance of the includeCurrentState parameter is impacted by the cache size, and therefore it is disabled by default.
@ClientCacheFailover
Rather than relying on receiving state, users can define a method with the @ClientCacheFailover annotation, which receives ClientCacheFailoverEvent parameter inside the client listener implementation. If the node where a Hot Rod client has registered a client listener fails, the Hot Rod client detects it transparently, and fails over all listeners registered in the node that failed to another node.
During this failover, the client may miss some events. To avoid this, the includeCurrentState parameter can be set to true. With this enabled a client is able to clear its data, receive all of the CacheEntryCreatedEvent instances, and cache these events with all keys. Alternatively, Hot Rod clients can be made aware of failover events by adding a callback handler. This callback method is an efficient solution to handling cluster topology changes affecting client listeners, and allows the client listener to determine how to behave on a failover. Near Caching takes this approach and clears the near cache upon receiving a ClientCacheFailoverEvent.
Example 8.18. @ClientCacheFailover
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
// ...
@ClientCacheFailover
public void handleFailover(ClientCacheFailoverEvent e) {
// Deal with client failover, e.g. clear a near cache.
}
}
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
@ClientListener
public class EventLogListener {
// ...
@ClientCacheFailover
public void handleFailover(ClientCacheFailoverEvent e) {
// Deal with client failover, e.g. clear a near cache.
}
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Note
The ClientCacheFailoverEvent is only thrown when the node that has the client listener installed fails.