Showing posts with label listeners. Show all posts
Showing posts with label listeners. Show all posts

Thursday, 14 December 2017

First steps with Vert.x and Infinispan - PUSH API (Part 2)

Welcome to the second in a multi-part series of blog posts about creating Eclipse Vert.x applications with Infinispan. In the previous blog post we have seen how to create a REST API. The purpose of this tutorial is to showcase how to create a PUSH API implemented with Vert.x and using Infinispan as a server.
All the code of this tutorial is available in this GitHub repository. The backend is a Java project using Maven, so all the needed dependencies can be found in the pom.xml. The front is a super simple react application.


PUSH API

Creating a REST API is very straightforward. But today, even if we are heavily using REST, we don't always want to use request/response or polling, but instead we want to push directly from the server to the client. In this example, we are going to create an API that pushes every new value inserted in the default cache of Infinispan. These values are cute names, as we did in the REST API example.

We are using two features here :
  • Infinispan client listeners
  • Vert.x bridge between the Event Bus and the browser
Infinispan Listeners provide a way to the client get notified when something happens in a cache.

The Event Bus Bridge that connects to the browser, uses SockJS. SockJS is a JavaScript library that provides a WebSocket API, but it can be used with browsers that don't support web-sockets (see the website of the project for more detailed information). Vert.x supports this library and creates a bridge between your browser and your back-end easily through the Event Bus.

Creating an Event Bus bridge


Vert.x is a reactive framework, which means that uses RxJava too, and provides a fancy API on top of it.

First, we are going to create a new verticle called SendCuteNamesAPI. This verticle extends the CacheAccessVerticle we created in the previous blog postCacheAccessVerticle initialises the connection with Infinispan using the Hot Rod protocol.

Now we need to create a SocketJSHandler. This handler has a method called bridge, where we configure some BridgeOptions. Obviously we don't want the client to be able to read everything traveling on the event bus, and this won't happen. We configure an address, 'cute-names', and we add the permission to read and write to this address.

This handler is passed to the event bus route, where the path is /eventbus/*.

Finally, we create a http server as we did in the REST API example. The difference is that instead of calling listen method, we call rxListen and subscribe.



Getting notified and publishing


Using Infinispan listeners is very easy.

First, we are going to create a class that has the @ClientListener annotation. The client listener has to be added to the cache client configuration. We add a protected method called addConfigToCache that will be called just after the initialisation of the defaultCache in the abstract CacheAccessVerticle. Verticles extending the abstract class can now add custom configuration to the client.

We want to be notified when a new entry is created. In this case, our listener has to contain a method with the @ClientCacheEntryCreated annotation on it. The signature of the method has to include a ClientCacheEntryCreatedEvent<String> parameter. This parameter will hold the 'key' of the entry that has been created.

Finally, we use the key to retrieve the name using the getAsync method and then publish the value in the Vert.x event bus to the address where the socket listener is permitted to readcute-names.


Now we can run the main method and whenever we post a new name, we will see in the logs that the client listener is notified!




Client code


We are going to create a super simple react application that will just display hello. React community is *huge*, so there are lot's of tutorials out there to create a hello world client application. This application has a single component that displays "Hello".

The react application runs calling npm install and npm start  in http://localhost:9000/.

Now we need to connect the client to the backend with SockJS. Vert.x provides a JavaScript library for that: vertx3-eventbus-client, built on top of SockJS. We create an EventBus object that will connect to http://localhost:8082/eventbus as we configured in the SendCuteNamesAPI. We register a handler on the 'cute-names' address. The body of the message will contain the new cute name published in the event bus. Every time the handler is called, we update the component's state, and it will be rendered.



Wrap up

We have learned how to create PUSH APIs with Vert.x, powered by Infinispan. The repository has some unit tests. Feedback is more than welcome to improve the code and the provided examples. I hope you enjoyed this tutorial ! On the next tutorials we will talk about Infinispan as the cluster manager for Vert.x. Stay tuned !


Monday, 12 October 2015

Functional Map API: Listeners

We continue with the blog series on the experimental Functional Map API which was released as part of Infinispan 8.0.0.Final. In this blog post we'll be focusing on how to listen for Functional Map events. For reference, here are the previous entries in the series:
  1. Functional Map Introduction
  2. Working with single entries
  3. Working with multiple entries
The first thing to notice about Functional Map listeners is that they only send events post-event, so that means the events are received after the event has happened. In contrast with Infinispan Cache listeners, there are no pre-event listener invocations. The reason pre-events are not available is because listeners are meant to be an opportunity to find out what has happened, and having pre-events can sometimes hint as if the listener was able to alter the execution of the operation, for which the listener is not really suited. If interested in pre-events or potentially altering the execution, plugging custom interceptors is the recommended solution.

Functional Map offers two type of event listeners: write-only operation listeners and read-write operation listeners.

Write-Only Listeners


Write listeners enable users to register listeners for any cache entry write events that happen in either a read-write or write-only functional map.

Listeners for write events cannot distinguish between cache entry created and cache entry modify/update events because they don’t have access to the previous value. All they know is that a new non-null entry has been written. However, write event listeners can distinguish between entry removals and cache entry create/modify-update events because they can query what the new entry’s value via ReadEntryView.find() method.

Adding a write listener is done via the WriteListeners interface which is accessible via both ReadWriteMap.listeners() and WriteOnlyMap.listeners() method. A write listener implementation can be defined either passing a function to onWrite(Consumer<ReadEntryView<K, V>>) method, or passing a WriteListener implementation to add(WriteListener<K, V>) method. Either way, all these methods return an AutoCloseable instance that can be used to de-register the function listener. Example and expected output:


Read-Write Listeners


Read-write listeners enable users to register listeners for cache entry created, modified and removed events, and also register listeners for any cache entry write events. Entry created, modified and removed events can only be fired when these originate on a read-write functional map, since this is the only one that guarantees that the previous value has been read, and hence the differentiation between create, modified and removed can be fully guaranteed.

Adding a read-write listener is done via the ReadWriteListeners interface which is accessible via ReadWriteMap.listeners() method. If interested in only one of the event types, the simplest way to add a listener is to pass a function to either onCreate, onModify or onRemove methods. Otherwise, if interested in multiple type of events, passing a ReadWriteListener implementation via add(ReadWriteListener<K, V>) is the easiest. As with write-listeners, all these methods return an AutoCloseable instance that can be used to de-register the listener.

Here's an example of adding a ReadWriteListener that handles multiple type of events:


Closing Notes


More listener event types are yet to be implemented for Functional API, such as expiration events or passivation/activation events. We are capturing this future work and other improvements under the ISPN-5704 issue.

We'd love to hear from you on how you are finding this new API. To provide feedback or report any problems with it, head to our user forums and create a post there.

In next blog post in the series, we'll be looking into how to pass per-invocation parameters to tweak operations.

Cheers,
Galder



Wednesday, 5 March 2014

Embedded Cluster Listeners in Infinispan 7.0.0.Alpha1

If you are following on the dev listing, you may have seen a design doc come through that details adding support for Clustered Listeners to Infinispan.  This features allows for listeners to be used in a distributed cache configuration.  I am happy to say that this feature is now in Infinispan 7.0.0.Alpha1 !

This feature is needed since local listeners in a distributed cache are only notified of events on the node where the data resides.  Therefore, clustered listeners allow for a single listener to receive any write notification (limited to CacheEntryCreatedEvent, CacheEntryModifiedEvent and CacheEntryRemovedEvent) that occurs in the cluster which is installed on 1 node.

Basic Example

Using a cluster listener is just as easy as a regular listener. Here is a simple use case that stores the events as it receives them. That is all that is required is just to set the property of your Listener annotated class to say clustered = true. There are other important changes in the rest of the document. Please let us know how you like the new cluster listeners ! Also if any issues are found, it is much appreciated to log those to JIRA.

Differences between Local and Cluster Listeners

Pre and post Notifications

In a local cluster listener, the listener is notified twice, before the operation is completed and after the entry is updated.  A cluster listener is ONLY notified after the operation is completed while still holding locks.  Therefore, the isPre method always returns false in a cluster listener.

Transaction begin and completion

In a transactional cache, local listeners are notified when a transaction begins and when it is completed (either through rollback or commit).  A cluster listener is never notified of anything occurring until after the data has been updated, and thus will only ever be notified of committed entries and also will not receive TransactionRegisteredEvent or TransactionCompletedEvent events.

API Changes


There are a few new API classes that have been added to allow for configuration and operation of cluster listeners.

Listener annotation


The existing org.infinispan.notifications.Listener annotation has had a couple properties added to it.
The new clustered property defines whether or not this listener is a cluster listener or not.  This means the listener will be sent all write modification events.

A cluster listener is not supported in an Invalidation cache.  Local or replicated caches can use a cluster listener though.  They will behave like a local cluster listener, except that replicated will be less performant.

The includeCurrentState property is also new and will provide a way for a listener when being registered to immediately be sent a CacheCreatedEvent for every entry in the cache.  This will be supported for both local and cluster listeners.  In a local listener it will only query the local data that is available, so in the case of a Distributed cache this will still only provide a possible subset of data.  However a clustered listener will retrieve the data from all nodes as needed.  A cache will still be available for writes during the includeCurrentState period.  However the notifications will be queued until all state has been first sent. NOTE: includeCurrentState is currently not implemented but is planned during this release still see ISPN-4068

KeyValueFilter


This is a new Filter class that can be used to filter events or other operations based on the key value and metadata of the updated object.

Converter


A converter is used to convert a given key, value, metadata entry to a resulting value. This is useful if your listener doesn't require the entire value and need just a portion from it. Or if the listener were to do some sort of translation, this would allow it to scale to each node instead of having to run the translation all on the node where the listener is registered.

Cache


The cache interface also has an additional overloaded method to allow for registering the previously mentioned KeyValueFilter and Converter with the Listener provided.  Note that either type of listener, cluster or local, may be used with any of the overloaded addListener methods on the Cache interface.
This new method is similar to the other addListener methods, but is specially optimized for use with cluster listeners in distributed mode. Whenever a modification occurs which would cause an event to be sent to the cluster notifier the KeyValueFilter is first ran to see if this event should even be sent to the listener. If it is then the converter will be used to convert result into whatever data is desired to send back to the listener. These combined allow for reducing overall network traffic for events that you don't want to get or reduce payload size by sending a different or subset of the value.

Events


There are some cases in Infinispan when it is unclear if a notification was properly raised in a non transactional cache. Due to this we have made available an additional value on the CacheEntryCreatedEvent, CacheEntryModifiedEvent, and CacheEntryRemovedEvent. This is to symbolize that this event could have been possibly duplicated or even changed types (CacheEntryModifiedEvent instead of CacheEntryCreatedEvent).
This should only return true if we had a node who was an owner go down while in the middle of processing the write.

Functional Changes


CacheEntryModified during creates


Prior to Infinispan 7.0, whenever a new entry was created, this would generate both CacheEntryCreated and CacheEntryModified events.  This has been changed now so that only a CacheEntryCreated event is raised to more consistently model what has occurred.

Monday, 9 September 2013

Infinispan 6.0.0.Alpha4 out with new CacheLoader/CacheWriter API!

Infinispan 6.0.0.Alpha4 is now with a few very important changes, particularly around cache stores. We've completely revamped the cache store/loader API to align it a bit better with JSR-107 (old CacheStore has become CacheWriter) and to simplify creation of new implementations. The new CacheLoader and CacheWriter should help implementors focus on the important operations and reduce the coding time. We've also created AdvancedCacheLoader and AdvancedCacheWriter in order to separate for bulk operations or purging for those implementations that wish optionally implement them. Expect a blog post from Mircea in the next few days providing many more details on this topic.

This new Infinispan version comes with other important goodies:
  • Rolling upgrades of a Infinsipan REST cluster
  • Support for Cache-Control headers for REST operations
  • Remote querying server modules and Hot Rod client update
  • REST and LevelDB stores added to Infinispan Server
  • KeyFilters can now be applied to Cache listeners
  • Allow Cache listener events to be invoked only on the primary data owner
For a complete list of features and fixes included in this release please refer to the release notes. Visit our downloads section to find the latest release and if you have any questions please check our forums, our mailing lists or ping us directly on IRC.

Cheers,
Galder