Remote Observation Module

Last modified by Vincent Massol on 2014/08/27 12:11

Provides the ability to automatically send and receive events through the network.

rom.png

Features

  • api to start/stop channels at runtime
  • add custom event converter
  • add custom network adapter implementation (default is based on JGroups)

Quick Example

Start/stop channel

RemoteObservationManager rom = componentManager.lookup(RemoteObservationManager.class);

// stop the channel with identifier channelid, the channel with configuration file channelid.xml
rom.stopChannel("channelid");
// start the channel with identifier channelid, the configuration file is releaded
rom.startChannel("channelid");

Setup

General configuration

Found in WEB-INF/xwiki.properties file

#-# [Since 2.0M3]
#-# Indicate if the network distribution module is enabled or not.
#-# By default remote events are disabled.
# observation.remote.enabled = false

#-# [Since 2.0M3]
#-# The list of events communication channels to start when the application starts.
#-# By default no channel is configured.
#-#
#-# The default remote event distribution implementation is using JGroups and you'll need to drop your JGroups channel
#-# xml files in the WEB-INF/observation/remote/jgroups/ directory. There's a README file in that directory with more
#-# information.
#-# Example: observation.remote.channels = public, cluster

#-# [Since 2.0M4]
#-# The implementation of network adapter to use.
#-# The default is jgroups.
#-#
#-# By default obnly jgroups is provided. To add one implements NetworkAdaptor componenet interface. The identifier provided in the configuration is matched with the component role hint.
#-# Example: observation.remote.networkadapter = jgroups

JGroups

Currently only JGroups implementation is provided.

The configuration for a channel is searched in the following order:

  1. WEB-INF/observation/remote/jgroups/<channel name>.xml
  2. <channel name>.xml in the root of jgroups jar file

Check remote event

Remote Observation Manager is supposed to emulate local event as much as possible but in some case you actually need to do different things depending if it's a remote or local event (invalidate a cache, avvoid doing twice something on the database, etc.).

For this you can use inject the component org.xwiki.observation.remote.RemoteObservationManagerContex.

import org.xwiki.observation.remote.RemoteObservationManagerContext;
[...]

public class MyListener implements EventListener
{
   @Inject
   private RemoteObservationManagerContext remoteObservationManagerContext;

   [...]

   @Override
   public void onEvent(Event event, Object source, Object data)
   {
       if (this.remoteObservationManagerContext.isRemoteState()) {
           // do something specific to remote event
       }
   }
}

From Remote Observation Manager module.

   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-observation-remote</artifactId>
     <version>${platform.version}</version>
   </dependency>

Extends Remote Observation Manager

Remote event manage is extendable in many ways depending of the need.

Add custom event converter

Each time an event is generated locally or received from network, Remote Event Manager try to converter it to the other side. By default a support of all document events and any fully serializable event is provided but since it's possible to add any kind of local event Remote Event Manager also provide a way to provide converter for any kind of event as well.

There are two kinds of converters:

  • the local to remote event converter: org.xwiki.observation.remote.converter.LocalEventConverter
  • the remote to local event converter: org.xwiki.observation.remote.converter.RemoteEventConverter

Most of the time when adding a new event you will need to make conversion both ways. The common way to do this is by extending AbstractEventConverter which provide a default priority and implements both LocalEventConverter and RemoteEventConverter.

public class ViewEventConverter extends AbstractEventConverter
{
   public int getPriority()
   {
       // default priority level
       return 1000;
   }

   public boolean toRemote(LocalEventData localEvent, RemoteEventData remoteEvent)
   {
       if (localEvent.getEvent() instanceof ActionExecutionEvent) {
            ActionExecutionEvent event = (ActionExecutionEvent) localEvent.getEvent();

           if ("view".equals(event.getActionName())) {
               // fill the remote event
               remoteEvent.setEvent(event);
           }

           return true;
       }

       return false;
   }

   public boolean fromRemote(RemoteEventData remoteEvent, LocalEventData localEvent)
   {
       if (localEvent.getEvent() instanceof ActionExecutionEvent) {
            ActionExecutionEvent event = (ActionExecutionEvent) localEvent.getEvent();

           if ("view".equals(event.getActionName())) {
               // fill the local event
               localEvent.setEvent(event);
           }

           return true;
       }

       return false;
   }
}

toRemote and fromRemote work the same way: converter manager call them in the priority order one by one and stop when the methods return true. If the converter event data is empty it mean the event should not be sent for remote event or given to Observation Manager for local events.

This converter is a combination local event converter and remote event converter. It's possible to implement just local or just remote. For example one would want to filter some kind of events or document.

Add custom network adaptor

The default implementation is based on JGroups, it possible to change just the network communication part easily by providing a new implementation of org.xwiki.observation.remote.NetworkAdapter and setting the role hint of this new component as value of the property observation.remote.networkadapter in the file xwiki.properties.

Provide custom JGroups listener

In JGroups based implementation it's possible to provide to set the Receiver to use channel by channel. This is useful for example to make a channel readonly when you want to send events in a public UDP broadcast and not do two ways clustering.

To do this implements the component interface org.xwiki.observation.remote.jgroups.JGroupsReceiver and make the role hint having the same name as the channel.

@Component
@Named("public")
public class PublicJGroupsReceiver implements JGroupsReceiver
{
   public byte[] getState()
   {
       return null;
   }

   public void receive(Message msg)
   {
       // do nothing, it's a readonly public channel
   }

   public void setState(byte[] state)
   {

   }

   public void block()
   {

   }

   public void suspect(Address suspectedMbr)
   {

   }

   public void viewAccepted(View newView)
   {

   }
}

JGroups

See http://www.jgroups.org and http://www.jboss.org/community/wiki/JGroups for more documentation on JGroups and its configuration files.

Debugging

There are types of logs you can enable to diagnose issues with clustering:

  • XWiki cluster logs
  • JGroups cluster logs

For both you'll need to edit the Log4J configuration (see Logging).

To enable XWiki cluster logs, add:

log4j.logger.org.xwiki.observation.remote=trace

To enable JGroups cluster logs, see the JGroups Logging Article.

Tutorial

Tags:
Created by Vincent Massol on 2010/12/08 19:09
    

Get Connected