<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<section id="Kernel.RPCService">
  <title>RPC Service</title>

    <para>The <emphasis>RPCService</emphasis> 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 <emphasis>RPCService</emphasis>
    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.</para>

    <para>The <emphasis>RPCService</emphasis> relies on 3 main interfaces
    which are:</para>

    <itemizedlist>
      <listitem>
        <para>The <emphasis>org.exoplatform.services.rpc.RPCService</emphasis>
        that defines the service itslef</para>
      </listitem>

      <listitem>
        <para>The
        <emphasis>org.exoplatform.services.rpc.RemoteCommand</emphasis> that
        defines the command that we can execute on other nodes.</para>
      </listitem>

      <listitem>
        <para>The
        <emphasis>org.exoplatform.services.rpc.TopologyChangeListener</emphasis>
        that defines the listeners that will be notified anytime the topology
        of the cluster changes.</para>
      </listitem>
    </itemizedlist>

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

    <para>To be usable, all the <emphasis>RemoteCommand</emphasis>s 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 <emphasis>RPCService</emphasis> is started.
    If you try to launch a command that has been registered but the
    <emphasis>RPCService</emphasis> is not yet launched, you will get an
    <emphasis>RPCException</emphasis> 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.</para>

    <para>See an example below:</para>

    <programlisting language="java">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()
   {
   }
}</programlisting>

    <para>In the previous example, we register the command
    <emphasis>sayHelloCommand</emphasis> in the constructor of
    <emphasis>MyService</emphasis> and we execute this command in the start
    method.</para>

    <note>
      <para>We expect to have one <emphasis>RPCService</emphasis> instance per
      <emphasis>PortalContainer</emphasis> in a portal mode and only one
      <emphasis>RPCService</emphasis> instance in a standalone mode</para>
    </note>

  <section id="Kernel.RPCService.Configuration">
    <title>Configuration</title>

    <para>The configuration of the <emphasis>RPCService</emphasis> 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):</para>

    <programlisting language="xml">&lt;configuration&gt;
....  
  &lt;component&gt;
    &lt;key&gt;org.exoplatform.services.rpc.RPCService&lt;/key&gt;
    &lt;type&gt;org.exoplatform.services.rpc.impl.RPCServiceImpl&lt;/type&gt;
    &lt;init-params&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-configuration&lt;/name&gt;
        &lt;value&gt;classpath:/udp.xml&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-cluster-name&lt;/name&gt;
        &lt;value&gt;RPCService-Cluster&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-default-timeout&lt;/name&gt;
        &lt;value&gt;0&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;allow-failover&lt;/name&gt;
        &lt;value&gt;true&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;retry-timeout&lt;/name&gt;
        &lt;value&gt;20000&lt;/value&gt;
      &lt;/value-param&gt;
    &lt;/init-params&gt;
  &lt;/component&gt;   
...
&lt;/configuration&gt;</programlisting>

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

    <programlisting language="xml">&lt;configuration&gt;
....  
  &lt;component&gt;
    &lt;key&gt;org.exoplatform.services.rpc.RPCService&lt;/key&gt;
    &lt;type&gt;org.exoplatform.services.rpc.jgv3.RPCServiceImpl&lt;/type&gt;
    &lt;init-params&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-configuration&lt;/name&gt;
        &lt;value&gt;classpath:/udp.xml&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-cluster-name&lt;/name&gt;
        &lt;value&gt;RPCService-Cluster&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;jgroups-default-timeout&lt;/name&gt;
        &lt;value&gt;0&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;allow-failover&lt;/name&gt;
        &lt;value&gt;true&lt;/value&gt;
      &lt;/value-param&gt;
      &lt;value-param&gt;
        &lt;name&gt;retry-timeout&lt;/name&gt;
        &lt;value&gt;20000&lt;/value&gt;
      &lt;/value-param&gt;
    &lt;/init-params&gt;
  &lt;/component&gt;   
...
&lt;/configuration&gt;</programlisting>

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

    <table>
      <title>Fields description</title>

      <tgroup cols="2">
        <tbody>
          <row>
            <entry><emphasis>jgroups-configuration</emphasis></entry>

            <entry>This is the location of the configuration of jgroups. This
            parameter is mandatory.</entry>
          </row>

          <row>
            <entry><emphasis>jgroups-cluster-name</emphasis></entry>

            <entry>This is the name of the cluster. This parameter is optional
            and its default value is <emphasis>RPCService-Cluster</emphasis>.
            Since we could have several instances of the
            <emphasis>RPCService</emphasis>, the final name will be
            "${jgroups-cluster-name}-${container-name}"</entry>
          </row>

          <row>
            <entry><emphasis>jgroups-default-timeout</emphasis></entry>

            <entry>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.</entry>
          </row>

          <row>
            <entry><emphasis>allow-failover</emphasis></entry>

            <entry>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
            <emphasis>executeCommandOnCoordinator</emphasis>. This parameter
            is optional and its default value is true.</entry>
          </row>

          <row>
            <entry><emphasis>retry-timeout</emphasis></entry>

            <entry>This parameter is the maximum amount of time to wait until
            the new coordinator is elected. This parameter is linked to the
            parameter <emphasis>allow-failover</emphasis>, and thus used in
            the exact same conditions. This parameter is optional and its
            default value is 20000. This parameter is expressed in
            milliseconds.</entry>
          </row>
        </tbody>
      </tgroup>
    </table>
  </section>

  <section id="Kernel.RPCService.SingleMethodCallCommand">
    <title>The SingleMethodCallCommand</title>

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

    <programlisting language="java">// 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);</programlisting>

    <para>This example:</para>

    <orderedlist>
      <listitem>
        <para>Register a <emphasis>SingleMethodCallCommand</emphasis> that
        will call <emphasis>getName()</emphasis> on the Object
        <emphasis>myService</emphasis> anytime the command will be
        executed.</para>
      </listitem>

      <listitem>
        <para>Execute the command synchronously on the coordinator, assuming
        that the same command (with the same id) has already been registered
        on the coordinator</para>
      </listitem>

      <listitem>
        <para>Print the name got from the coordinator</para>
      </listitem>
    </orderedlist>

    <note>
      <para>As any <emphasis>RemoteCommand</emphasis>, it has to be registered
      before being executed and before the <emphasis>RPCService</emphasis> is
      launched.</para>
    </note>

    <note>
      <para>As any <emphasis>RemoteCommand</emphasis>, the command can be
      executed only once the <emphasis>RPCService</emphasis> is
      launched.</para>
    </note>

    <note>
      <para>The <emphasis>SingleMethodCallCommand</emphasis> only allow public
      methods, if you try to register a non public method an
      <emphasis>RPCException</emphasis> will be thrown at creation
      level.</para>
    </note>
  </section>
</section>
