/*
 * Copyright (C) 2010 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.exoplatform.services.rpc;

import java.io.Serializable;
import java.util.List;

/**
 * This service provides mechanism to communicate with the other cluster nodes. This service will
 * be based of JGroups as an underlying Transport.
 * @author <a href="mailto:nicolas.filotto@exoplatform.com">Nicolas Filotto</a>
 * @version $Id$
 */
public interface RPCService
{

   /**
    * The permission needed to access to any methods of the RPCService
    */
   public static final RuntimePermission ACCESS_RPC_SERVICE_PERMISSION = new RuntimePermission("accessRPCService");

   /**
    * Executes a command on all the cluster nodes. This method is equivalent to the other method of the
    * same type but with the default timeout. The command must be registered first otherwise an 
    * {@link RPCException} will be thrown.
    *
    * @param command The command to execute on each cluster node
    * @param synchronous if true, sets group request mode to org.jgroups.blocks.GroupRequest#GET_ALL,
    *  and if false sets it to org.jgroups.blocks.GroupRequest#GET_NONE.
    * @param args an array of {@link Serializable} objects corresponding to parameters of the command 
    * to execute remotely
    * @return a list of responses from all the members of the cluster. If we met an exception on a given node, 
    * the RPCException will be the corresponding response of this particular node
    * @throws RPCException in the event of problems.
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   List<Object> executeCommandOnAllNodes(RemoteCommand command, boolean synchronous, Serializable... args)
      throws RPCException, SecurityException;

   /**
    * Executes a command synchronously on all the cluster nodes. The command must be registered first otherwise an 
    * {@link RPCException} will be thrown.
    *
    * @param command The command to execute on each cluster node
    * @param timeout a timeout after which to throw a replication exception.
    * @param args an array of {@link Serializable} objects corresponding to parameters of the command 
    * to execute remotely
    * @return a list of responses from all the members of the cluster. If we met an exception on a given node, 
    * the RPCException will be the corresponding response of this particular node
    * @throws RPCException in the event of problems.
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   List<Object> executeCommandOnAllNodes(RemoteCommand command, long timeout, Serializable... args)
      throws RPCException, SecurityException;

   /**
    * Executes a command on the coordinator only. This method is equivalent to the other method of the
    * same type but with the default timeout. The command must be registered first otherwise an 
    * {@link RPCException} will be thrown.
    *
    * @param command The command to execute on the coordinator node
    * @param synchronous if true, sets group request mode to org.jgroups.blocks.GroupRequest#GET_ALL,
    * and if false sets it to org.jgroups.blocks.GroupRequest#GET_NONE.
    * @param args an array of {@link Serializable} objects corresponding to parameters of the command 
    * to execute remotely
    * @return the response of the coordinator.
    * @throws RPCException in the event of problems.
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   Object executeCommandOnCoordinator(RemoteCommand command, boolean synchronous, Serializable... args)
      throws RPCException, SecurityException;

   /**
    * Executes a command synchronously on the coordinator only. The command must be registered first otherwise an 
    * {@link RPCException} will be thrown.
    *
    * @param command The command to execute on the coordinator node
    * @param timeout a timeout after which to throw a replication exception.
    * @param args an array of {@link Serializable} objects corresponding to parameters of the command 
    * to execute remotely
    * @return the response of the coordinator.
    * @throws RPCException in the event of problems.
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   Object executeCommandOnCoordinator(RemoteCommand command, long timeout, Serializable... args) throws RPCException,
      SecurityException;

   /**
    * Register a new {@link RemoteCommand} instance, it will be mapped to its id. If a command with the
    * same Id has already been registered, a warning will be printed into the log file and the new
    * command will replace the old one.
    * @param command the instance of the {@link RemoteCommand} to register
    * @return the command itself if it could be registered null otherwise 
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   RemoteCommand registerCommand(RemoteCommand command) throws SecurityException;

   /**
    * Unregister a {@link RemoteCommand} instance, if the id is known or the instance itself is known
    * otherwise it will be ignored
    * @param command the command to unregister
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   void unregisterCommand(RemoteCommand command) throws SecurityException;
   
   /**
    * Indicates whether the local node is the coordinator of the cluster
    * @return <code>true</code> if the coordinator is the coordinator, <code>false</code> otherwise
    * throws RPCException in case the {@link RPCService} is in an illegal state
    */
   boolean isCoordinator() throws RPCException;
   
   /**
    * Register a new {@link TopologyChangeListener}
    * @param listener the listener to be registered
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   void registerTopologyChangeListener(TopologyChangeListener listener) throws SecurityException;
   
   /**
    * Unregister a {@link TopologyChangeListener} if it exists
    * @param listener the listener to unregister
    * @throws SecurityException if the {@link SecurityManager} is installed and the call method
    * doesn't have the {@link RuntimePermission} <code>ACCESS_RPC_SERVICE_PERMISSION</code>
    */
   void unregisterTopologyChangeListener(TopologyChangeListener listener) throws SecurityException;
}