JBoss.orgCommunity Documentation

Chapter 69. RPC Service

69.1. Configuration
69.2. The SingleMethodCallCommand

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 capabilities 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 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

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.


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: