eXo Platform 3.5

## Chapter 69. RPC Service

## Description

The RPCService is only needed in a cluser environment, it is used to communicate with the other cluster nodes. It allows to execute a command on all the cluster nodes or on the coordinator i.e. the oldest node in the cluster. The RPCService has been designed to rely on JGroups capabilites and should not be used for heavy load. It can be used for example to notify other nodes that something happened or to collect some information from the other nodes.

The RPCService relies on 3 main interfaces which are:

- The org.exoplatform.services.rpc.RPCService that defines the service itslef

- The org.exoplatform.services.rpc.RemoteCommand that defines the command that we can execute on other nodes.

- The org.exoplatform.services.rpc.TopologyChangeListener that defines the listeners that will be notified anytime the topology of the cluster changes.

The arguments that will be given to the RemoteCommand must be Serializable and its return type also in order to prevent any issue due to the serialization. To prevent to execute any RemoteCommand that could be malicious and to allow to use non Serializable command, you need to register the command first before using it. Since the service will keep only one instance of RemoteCommand per command Id, the implementation of the RemoteCommand must be thread safe.

To be usable, all the RemoteCommands must be registered before being used on all the cluster nodes, which means that the command registration must be done in the constructor of your component in other words before that the RPCService is started. If you try to launch a command that has been registered but the RPCService is not yet launched, you will get an RPCException due to an illegal state. This has for consequences that you will be able to execute a command only once your component will be started.

See an example below:

    public class MyService implements Startable

    {

       private RPCService rpcService;

       private RemoteCommand sayHelloCommand;

       public MyService(RPCService rpcService)

       {

          this.rpcService = rpcService;

          // Register the command before that the RPCService is started

          sayHelloCommand = rpcService.registerCommand(new RemoteCommand()

          {

             public Serializable execute(Serializable[] args) throws Throwable

             {

                System.out.println("Hello !");

                return null;

             }

             public String getId()

             {

                return "hello-world-command";

             }

          });

       }

       public void start()

       {

          // Since the RPCService is a dependency of RPCService, it will be started before

          // so I can execute my command

          try

          {

             // This will make all the nodes say "Hello !"

             rpcService.executeCommandOnAllNodes(sayHelloCommand, false);

          }

          catch (SecurityException e)

          {

             e.printStackTrace();

          }

          catch (RPCException e)

          {

             e.printStackTrace();

          }

       }

       public void stop()

       {

       }

    }

In the previous example, We register the command sayHelloCommand in the constructor of MyService and we execute this command in the start method.

### Note

We expect to have one RPCService instance per PortalContainer in a portal mode and only one RPCService instance in a standalone mode

## Configuration

The configuration of the RPCService should be added only in a cluster environment. See below an example of configuration in case you intend to use JGroups 2 (which is mandatory if you use JBoss Cache as underlying cache):

    <configuration>

    ....

      <component>

        <key>org.exoplatform.services.rpc.RPCService</key>

        <type>org.exoplatform.services.rpc.impl.RPCServiceImpl</type>

        <init-params>

          <value-param>

            <name>jgroups-configuration</name>

            <value>classpath:/udp.xml</value>

          </value-param>

          <value-param>

            <name>jgroups-cluster-name</name>

            <value>RPCService-Cluster</value>

          </value-param>

          <value-param>

            <name>jgroups-default-timeout</name>

            <value>0</value>

          </value-param>

          <value-param>

            <name>allow-failover</name>

            <value>true</value>

          </value-param>

          <value-param>

            <name>retry-timeout</name>

            <value>20000</value>

          </value-param>

        </init-params>

      </component>

    ...

    </configuration>

See below an example of configuration in case you intend to use JGroups 3 (which is mandatory if you use Infinispan as underlying cache):

    <configuration>

    ....

      <component>

        <key>org.exoplatform.services.rpc.RPCService</key>

        <type>org.exoplatform.services.rpc.jgv3.RPCServiceImpl</type>

        <init-params>

          <value-param>

            <name>jgroups-configuration</name>

            <value>classpath:/udp.xml</value>

          </value-param>

          <value-param>

            <name>jgroups-cluster-name</name>

            <value>RPCService-Cluster</value>

          </value-param>

          <value-param>

            <name>jgroups-default-timeout</name>

            <value>0</value>

          </value-param>

          <value-param>

            <name>allow-failover</name>

            <value>true</value>

          </value-param>

          <value-param>

            <name>retry-timeout</name>

            <value>20000</value>

          </value-param>

        </init-params>

      </component>

    ...

    </configuration>

The implementation for JGroups 3 is available in the library exo.kernel.component.ext.rpc.impl.jgroups.v3-X.Y.Z.jar.

jgroups-configuration | This is the location of the configuration of jgroups. This parameter is mandatory.
jgroups-cluster-name | This is the name of the cluster. This parameter is optional and its default value is RPCService-Cluster. Since we could have several instances of the RPCService, the final name will be "${jgroups-cluster-name}-${container-name}"
jgroups-default-timeout | This is the default timeout to use if the timeout is not given, if no response could be get after this timeout an exception will be thrown. This parameter is optional and its default value is 0 which means that we don't use any timeout by default. This parameter is expressed in milliseconds.
allow-failover | This is parameter indicates whether a command on the coordinator needs to be relaunched or not if the coordintator seems to have left the cluster. This parameter only affects the behavior of the methods executeCommandOnCoordinator. This parameter is optional and its default value is true.
retry-timeout | This parameter is the maximum amount of time to wait until the new coordinator is elected. This parameter is linked to the parameter allow-failover, and thus used in the exact same conditions. This parameter is optional and its default value is 20000. This parameter is expressed in milliseconds.

## The SingleMethodCallCommand

Most of the time we only need to call a method on a given object, this can be done thanks to the org.exoplatform.services.rpc.SingleMethodCallCommand which is the implementation of a RemoteCommand proposed by default. This command will dynamically execute a method on a given object.

    // Register the command first (to be done before that the RPCService has been started)

    RemoteCommand commandGetName = rpcService.registerCommand(new SingleMethodCallCommand(myService, "getName"));

    ...

    // Execute the command on the coordinator (can be done only after having started the RPCService)

    String name = rpcService.executeCommandOnCoordinator(commandGetName, true);

    // Print the name

    System.out.println("Name : " + name);

This example:

1. Register a SingleMethodCallCommand that will call getName() on the Object myService anytime the command will be executed.

2. Execute the command synchronously on the coordinator, assuming that the same command (with the same id) has already been registered on the coordinator

3. Print the name got from the coordinator

### Note

As any RemoteCommand, it has to be registered before being executed and before the RPCService is launched.

### Note

As any RemoteCommand, the command can be executed only once the RPCService is launched.

### Note

The SingleMethodCallCommand only allow public methods, if you try to register a non public method an RPCException will be thrown at creation level.