Client Notifications
This document is referring to a past Scout release. Please click here for the recent version. |
In a scout application, typically, the scout client requests some data from the scout server. Sometimes, however, the communication needs to go the other way: The scout server needs to inform the scout client about something. With client notifications it is possible to do so.
Examples
Example scenarios for client notifications are:
-
some data shared by client and server has changed (e.g. a cache on the client is no longer up-to-date, or a shared variable has changed)
-
a new incoming phone call is available for a specific client and should be shown in the GUI
-
a user wants to send a message to another user
Scout itself uses client notifications to synchronize code type and permission caches and session shared variables.
Data Flow
A client notification message is just a serializable object. It is published on the server and can be addressed either to all client nodes or only to a specific session or user. On the UI server side, handlers can be used to react upon incoming notifications.
Client notification handlers may change the state of the client model. In case of visible changes in the UI, these changes are automatically reflected in the UI.
In case of multiple server nodes, the client notifications are synchronized using cluster notifications to ensure that all UI servers receive the notifications.
Push Technology
Client notifications are implemented using long polling
as described below, because long polling works reliably in most corporate networks with proxy servers between server and client
as well as with security policies that do not allow server push.
With long polling, the client requests notifications from the server repeatedly. If no new notifications are available on the server, instead of sending an empty response, the server holds the request open and waits until new notifications are available or a timeout is reached.
In addition to the long polling mechanism, pending client notifications are also transferred to the client along with the response of regular client requests.
Components
A client notification can be published on the server using the ClientNotificationRegistry
.
Publishing can be done either in a non-transactional or transactional way (only processed, when the transaction is committed).
The UI Server either receives the notifications via the ClientNotificationPoller
or in case of transactional notifications
together with the response of a regular service request. The notification is then dispatched to the corresponding handler.
When a client notifications is published on the server, it is automatically synchronized with the other server nodes (by default).
Publishing
BEANS.get(ClientNotificationRegistry.class).putForUser("admin", new PersonTableChangedNotification());
There are several options to choose from when publishing a new client notification:
ClientNotificationAddress
The ClientNotificationAddress
determines which how the client notification needs to be dispatched and handled.
A client notification can be addressed to
-
all nodes
-
all sessions
-
one or more specific session
-
one or more specific user
Transactional vs. Non-transactional
Client notifications can be published in a transactional or non-transactional way.
-
Transactional means that the client notifications are only published once the transaction is committed. If the transaction fails, client notifications are disregarded.
-
Non-transactional means that client notifications are published immediately without considering any transactions.
Distributing to all Cluster Nodes
Generally, it makes sense to distribute the client notifications automatically to all other server cluster nodes (if available). This is achieved using ClusterNotifications
.
It is however also possible to publish client notifications without cluster distribution. E.g. in case of client notifications already received from other cluster nodes.
Coalescing Notifications
It is possible that a service generates a lot of client notifications that are obsolete once a newer notification is created. In this case a coalescer can be created to reduce the notifications:
public class BookmarkNotificationCoalescer implements ICoalescer<BookmarkChangedClientNotification> {
@Override
public List<BookmarkChangedClientNotification> coalesce(List<BookmarkChangedClientNotification> notifications) {
// reduce to one
return CollectionUtility.arrayList(CollectionUtility.firstElement(notifications));
}
}
Handling
The ClientNotificationDispatcher
is responsible for dispatching the client notifications to the correct handler.
Creating a Client Notification Handler
To create a new client notification handler for a specific client notification, all you need to do is creating a class implementing
org.eclipse.scout.rt.shared.notification.INotificationHandler<T>
, where T
is the type (or subtype) of the notification to handle.
The new handler does not need to be registered anywhere. It is available via jandex class inventory.
MessageNotifications
public class MessageNotificationHandler implements INotificationHandler<MessageNotification> {
@Override
public void handleNotification(final MessageNotification notification) {
Handling Notifications Temporarily
Sometimes it is necessary to start and stop handling notification dynamically, (e.g. when a form is opened) in this case AbstractObservableNotificationHandler
can be used to add and remove listeners.
Asynchronous Dispatching
Dispatching is always done asynchronously. However, in case of transactional notifications, a service call blocks until all transactional notifications returned with the service response are handled.
This behavior was implemented to simplify for example the usage of shared caches:
CodeService cs = BEANS.get(CodeService.class);
cs.reloadCodeType(UiThemeCodeType.class);
//client-side reload triggered by client notifications is finished
List<? extends ICode<String>> reloadedCodes = cs.getCodeType(UiThemeCodeType.class).getCodes();
In the example above, it is guaranteed, that the codetype is up-to-date as soon as reloadCodeType is finished.
Updating Scout Model
Notification handlers are never called from a scout model thread. If the scout model needs to be updated when handling notifications, a model job needs to be created for that task.
@Override
public void handleNotification(final MessageNotification notification) {
ModelJobs.schedule(() -> {
IDesktop desktop = ClientSessionProvider.currentSession().getDesktop();
// e.g. send dataChanged event to UI listeners
desktop.dataChanged(notification.getMessage());
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()));
}
Make sure to always run updates to the scout models in a model job (forms, pages, …): Use ModelJobs.schedule(…) where necessary in notification handlers. |