Skip to main content
Jump to: navigation, search

Scout/HowTo/4.0/Client notifications

< Scout‎ | HowTo‎ | 4.0


Scout
Wiki Home
Website
DownloadGit
Community
ForumsBlogTwitterG+
Bugzilla
Bugzilla


This how-to describes how to implement client notifications as described on the Client Notification Concept page.

Summary

The following classes need to be changed or added to support this:

shared

   org.eclipsescout.demo.minicrm.shared\META-INF\MANIFEST.MF
 org.eclipsescout.demo.minicrm.shared.CreateNotificationPermission
 org.eclipsescout.demo.minicrm.shared.notification.XxxxNotification (one for each event, where Xxxx is the name of the event)
 org.eclipsescout.demo.minicrm.shared.services.process.INotificationService

server

  org.eclipsescout.demo.minicrm.server\plugin.xml
 org.eclipsescout.demo.minicrm.server.services.custom.security.AccessControlService
 org.eclipsescout.demo.minicrm.server.NotificationService

client

   org.eclipsescout.demo.minicrm.client\plugin.xml
 org.eclipsescout.demo.minicrm.client.ClientSession
 org.eclipsescout.demo.minicrm.client.ui.desktop.Desktop
 org.eclipsescout.demo.minicrm.client.services.IMyNotificationConsumerService
 org.eclipsescout.demo.minicrm.client.services.MyNotificationConsumerService

The changes in detail are as follows:

shared

org.eclipsescout.demo.minicrm.shared\META-INF\MANIFEST.MF

Add the notification package to the exported packages:

Export-Package: org.eclipsescout.demo.minicrm.shared,
  org.eclipsescout.demo.shared.notification,
  ... remaining packages ...

org.eclipsescout.demo.minicrm.shared.CreateNotificationPermission

Create this file with the content as follows:

public class CreateNotificationPermission extends BasicPermission {
  private static final long serialVersionUID = 0L;

  public CreateNotificationPermission() {
   super(CreateNotificationPermission.class.getName());
  }
}


org.eclipsescout.demo.minicrm.shared.notification.XxxxNotification

Create a notification class for each distinct event you want to be able to signal. Optionally, add members and a constructor if you need to pass information with the notification:

public class XxxxNotification extends AbstractClientNotification {
  private static final long serialVersionUID = 1L;
  /* optional private members */
 
  /* optional constructor */
  public XxxxNotification(/* parameters as needed */) {
    super();
    /* assigning parameters to private members as needed */
  }

  @Override
  public boolean coalesce(IClientNotification existingNotification) {
    return false;
  }
}


org.eclipsescout.demo.minicrm.shared.services.process.INotificationService

Create the skeleton for this interface:

@InputValidation(IValidationStrategy.NO_CHECK.class)
public interface INotificationService extends IService {
}

For each event that you want to notify independently, add a method (corresponding to the notifications above; add parameters if needed):

public void sendXxxx(/* optional parameters */) throws ProcessingException;


server

org.eclipsescout.demo.minicrm.server\plugin.xml

In the extension section that contains the services, add the NotificationService:

     <service
         factory="org.eclipse.scout.rt.server.services.ServerServiceFactory"
         class="org.eclipsescout.demo.minicrm.server.services.process.NotificationService"
         session="org.eclipsescout.demo.minicrm.server.ServerSession">
   </service>

org.eclipsescout.demo.minicrm.server.services.common.security.AccessControlService

In the method execLoadPermissions() add the permission to create notifications and wrap the whole remainder of the class (defining user based permissions) in a check for null:

 @Override
 protected Permissions execLoadPermissions() {
    Permissions permissions = new Permissions();
    permissions.add(new RemoteServiceAccessPermission("*.shared.*", "*"));
    permissions.add(new CreateNotificationPermission());
 
    if (ServerSession.get().getPersonNr() != null) {
      // backdoor: the first user may do everything?
      if (ServerSession.get().getPersonNr().equals(1L)) {
        logger.warn("backdoor used: person nr 1 was granted all permissions");
        permissions.add(new AllPermission());
      }
      else {
        try {
 
          // get simple class names from the databse
          StringArrayHolder permission = new StringArrayHolder();
          SQL.selectInto("" +
              "SELECT DISTINCT P.PERMISSION_NAME " +
              "FROM ROLE_PERMISSION P, USER_ROLE R " +
              "WHERE R.USER_NR = :personNr " +
              "AND R.ROLE_NR = P.ROLE_NR " +
              "INTO :permission ",
              new NVPair("permission", permission));
 
          // create a map from simple class names to qualified class names
          HashMap<String, String> map = new HashMap<String, String>();
          for (BundleClassDescriptor descriptor : SERVICES.getService(IPermissionService.class).getAllPermissionClasses()) {
            map.put(descriptor.getSimpleClassName(), descriptor.getClassName());
          }
 
          // instantiate the real permissions and assign them
          for (String simpleClass : permission.getValue()) {
            try {
              permissions.add((Permission) Class.forName(map.get(simpleClass)).newInstance());
            }
            catch (Exception e) {
              logger.error("cannot find permission " + simpleClass + ": " + e.getMessage());
            }
          }
        }
        catch (ProcessingException e) {
          logger.error("cannot read permissions: " + e.getStackTrace());
        }
      }
 
    }
    return permissions;
  }

org.eclipsescout.demo.minicrm.server.services.process.NotificationService

Create the skeleton of this class:

public class NotificationService extends AbstractService implements INotificationService {
  private static IScoutLogger logger = ScoutLogManager.getLogger(NotificationService.class);
  private final static long TIMEOUT = 1000 * 60 * 10; // 10min
}

For each method that was added to the interface, add the implementation:

 @Override
public void sendXxxx(/* parameters as needed */) throws ProcessingException {
 if (!ACCESS.check(new CreateNotificationPermission())) {
   throw new VetoException(TEXTS.get("AuthorizationFailed"));
 }
 logger.info("queue 'Xxxx' notification on server");
 IClientNotificationService service = SERVICES.getService(IClientNotificationService.class);
 service.putNotification(new XxxxNotification(/* your parameters */), new AllUserFilter(TIMEOUT));
}


client

org.eclipsescout.demo.minicrm.client\plugin.xml

In the extension section that contains the services and proxies, add the service for the MyNotificationConsumerService and the proxy for the MyNotificationService interface:

     <service
         factory="org.eclipse.scout.rt.client.services.ClientServiceFactory"
         class="org.eclipsescout.demo.minicrm.client.services.MyNotificationConsumerService"
         session="org.eclipsescout.demo.minicrm.client.ClientSession">
   </service>
   <proxy
         factory="org.eclipse.scout.rt.client.services.ClientProxyServiceFactory"
         class="org.eclipsescout.demo.minicrm.shared.services.process.INotificationService">
   </proxy>

org.eclipsescout.demo.minicrm.client.ui.desktop.Desktop

Add the following method, allowing access to the Desktop object:

  public static Desktop get() {
  return (Desktop) ClientSyncJob.getCurrentSession().getDesktop();
 }

Add other getters to objects you might need in your MyNotificationConsumerService.

org.eclipsescout.demo.minicrm.client.ClientSession

Modify the execLoadSession() method to enable client notifications:

  @Override
 public void execLoadSession() throws ProcessingException {
  setServiceTunnel(new HttpServiceTunnel(this, getBundle().getBundleContext().getProperty("server.url")));
 
  //pre-load all known code types
  CODES.getAllCodeTypes(org.eclipsescout.demo.minicrm.shared.Activator.PLUGIN_ID);
 
  // turn client notification polling on
  getServiceTunnel().setClientNotificationPollInterval(2000L);
 
  IMyNotificationConsumerService notificationHandlerService = SERVICES.getService(IMyNotificationConsumerService.class);
  SERVICES.getService(IClientNotificationConsumerService.class).addClientNotificationConsumerListener(this, notificationHandlerService);
 
  setDesktop(new Desktop());
 }

Modify the execStoreSession() method to end client notification:

  @Override
 public void execStoreSession() throws ProcessingException {
  ClientSession.get().getServiceTunnel().setClientNotificationPollInterval(-1);
 }

org.eclipsescout.demo.minicrm.client.services.IMyNotificationConsumerService

Create this empty interface:

public interface IMyNotificationConsumerService extends IService, IClientNotificationConsumerListener {
}

org.eclipsescout.demo.minicrm.client.services.MyNotificationConsumerService

Create the MyNotification consumer class. Add a block for each event you have defined in the shared project:

public class MyNotificationConsumerService extends AbstractService implements IMyConsumerService {
  private static IScoutLogger logger = ScoutLogManager.getLogger(MyNotificationConsumerService.class);
 
  @Override
  public void handleEvent(ClientNotificationConsumerEvent e, boolean sync) {
    logger.info("received client notification event for user '" + ClientSyncJob.getCurrentSession().getUserId() + "'");
 
    final IClientNotification notification = e.getClientNotification();
    final IClientSession session = ClientSyncJob.getCurrentSession();
 
    // deal with notification in async jobs to prevent blocking of the model thread
    if (notification instanceof XxxxNotification) {
      new ClientAsyncJob("async wrapper (Xxxx)", session) {
        @Override
        protected void runVoid(IProgressMonitor monitor) throws Throwable {
          new ClientSyncJob("Xxxx", session) {
            @Override
            protected void runVoid(IProgressMonitor monitor1) throws Throwable {
              Desktop desktop = Desktop.get();
              if (desktop != null) {
                /* do whatever is needed to react to this event */
              }
            }
          }.schedule();
        }
      }.schedule();
    }
    else if (notification instanceof XxxxNotification) {
      new ClientAsyncJob("async wrapper (Xxxx)", session) {
        @Override
        protected void runVoid(IProgressMonitor monitor) throws Throwable {
          new ClientSyncJob("Xxxx", session) {
            @Override
            protected void runVoid(IProgressMonitor monitor1) throws Throwable {
              Desktop desktop = Desktop.get();
              if (desktop != null) {
                /* do whatever is needed to react to this event */
              }
            }
          }.schedule();
        }
      }.schedule();
    }
  }
}


Triggering an event

In order to trigger a client notification (either from another client or from the server), use the following line:

  SERVICES.getService(INotificationService.class).sendXxxx(/* optional parameters */);

Back to the top