/*
 * Fichero:     ServicioGenericoP2P.java
 * Fecha:       20/10/06
 * Autor:       Alvaro Sarmiento Losada
 * Asignatura:  Proyecto Fin de Carrera
 * Descripción: Contiene la clase ServicioGenericoP2P, que representa a un 
 *              servicio JXTA de propósito general.
 *
 * Este fichero pertenece al proyecto fin de carrera del autor,
 * consistente en el estudio de JXTA y su aplicación al comercio 
 * electrónico b2b.
 */

package com.pfc.lib.p2p;

import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.peergroup.PeerGroup;
import net.jxta.id.IDFactory;
import net.jxta.id.ID;
import net.jxta.platform.ModuleClassID;
import net.jxta.protocol.ModuleSpecAdvertisement;
import net.jxta.protocol.ModuleClassAdvertisement;
import net.jxta.protocol.PipeAdvertisement;

import java.io.IOException;

/**
 * Clase abstracta que define el comportamiento básico de un servicio JXTA.
 * Esta clase representa una forma alternativa y efectiva de implementar
 * servicios sencillos en JXTA, pudiendo sustituir a la foma tradicional
 * explicada en los tutoriales.
 * <p>
 * La clase que implemente un servicio debe heredar de esta clase y definir
 * todos los métodos declarados como abstract. Esta clase solo define el
 * comportamiento a la hora de inicializar el servicio.
 *
 * @author  Alvaro Sarmiento Losada 
 * @see     ConfiguradorServicioP2P
 */
public abstract class ServicioGenericoP2P extends Object {
    
    /** El anuncio de Clase de Módulo de este servicio JXTA */
    protected ModuleClassAdvertisement miMCAdv;
	
    /** El anuncio de Especificación de Módulo de este servicio JXTA */ 
    protected ModuleSpecAdvertisement miMSAdv;

    /** El anuncio de Tubería de este servicio JXTA */
    protected PipeAdvertisement miPipeAdv;

    /** Referencia al propio grupo de peers */
    protected PeerGroup miPeerGroup;

    /** El servicio de búsqueda que usa este servicio */
    protected DiscoveryService miDiscoSvc;
    
    /**
     * Constructor básico de la clase.
     */
    protected ServicioGenericoP2P() {
        miMCAdv = null;
        miMSAdv = null;
        miPipeAdv = null;
    }
        
    /**
     * Constructor que inicializa las variables de clase básicas del servicio
     * Hay que suministrarle una  referencia al objeto {@link PeerGroup} que 
     * se este usando en este peer.
     *
     * @param  pg el <code>PeerGroup</code> del propio peer.
     */
    public ServicioGenericoP2P(PeerGroup pg) {
        this();
        miPeerGroup = pg;
        miDiscoSvc = pg.getDiscoveryService();        
    }

    /**
     * Inicializa el servicio JXTA prestado por este objeto, construyendo
     * los anuncios JXTA correspondientes y publicándolos en la red.
     *
     * @throws ExcepcionP2P si ocurre algún error al inicializar el 
     *                      servicio JXTA.
     */
    public abstract void inicializarServicio() throws ExcepcionP2P;

    /**
     * Comienza la ejecución del servicio prestado por este objeto.
     *
     * @throws ExcepcionP2P si ocurre algún error fatal durante la ejecución
     *                      del servicio.
     */
    public abstract void ejecutarServicio() throws ExcepcionP2P;

   
    /**
     * Inicializa el servicio en la red JXTA dado un objeto
     * {@link ConfiguradorServicioP2P}. Crea y publica los anuncios JXTA de
     * Clase de Módulo y de Especificación de Módulo (con el de Tubería
     * adjunto) con los parámetros especificados por <code>config</code>.
     */
    
    protected void inicializarServicio(ConfiguradorServicioP2P config) 
                                                          throws ExcepcionP2P {

        /* 
         * Se realiza la inicializacion del servicio de esta forma:
         *   - Se crea un anuncio de Clase de Módulo y se publica
         *   - Se crea un anuncio de Especificación de Módulo 
         *   - Se crea un anuncio de Tubería y se adjunta al de Especificación 
         *     de Módulo.
         *   - Se publica el anuncio de Especificación de Módulo.
         */

        try {				
            /* 
             * Se crea el anuncio de Clase de Módulo, y se inicializa a los 
             * valores suministrados al método el objeto configurador.
             */
            miMCAdv = (ModuleClassAdvertisement) AdvertisementFactory.newAdvertisement( ModuleClassAdvertisement.getAdvertisementType());
            //Se establece el nombre de la Clase de Módulo.
            miMCAdv.setName( config.getNombreClaseModulo() );
            //Se establece la descripción de la Clase de Módulo.
            miMCAdv.setDescription( config.getDescripcionClaseModulo() );
            //Se crea un nuevo identificador de Clase de Módulo.
            ModuleClassID mcID = IDFactory.newModuleClassID();
            miMCAdv.setModuleClassID(mcID);

            /* Se publica el anuncio de Clase de Modulo */
            miDiscoSvc.publish(miMCAdv);
            miDiscoSvc.remotePublish(miMCAdv, DiscoveryService.ADV);

            /* 
             * Se crea el anuncio de Especificación de Módulo, y se inicializa 
             * a los valores suministrados al metodo mediante el objeto 
             * configurador.
             */
            miMSAdv = (ModuleSpecAdvertisement) AdvertisementFactory.newAdvertisement( ModuleSpecAdvertisement.getAdvertisementType());
            //Se establece el nombre del servicio en la Especificación de Módulo
            miMSAdv.setName( config.getNombreServicio() );
            //Se establece la versión del servicio en la Espec. de Módulo
            miMSAdv.setVersion( config.getVersionServicio() );
            //Se establece el creador del servicio en la Espec. de Módulo
            miMSAdv.setCreator( config.getNombreCreador() );
            //Se establece el URI del servicio en la Especificación de Módulo
            miMSAdv.setSpecURI( config.getUriServicio() );
            //Se crea un nuevo identificador para la Especificación de Módulo
            miMSAdv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));

            /* 
             * Se crea el anuncio de Tubería, y se inicializa a los valores 
             * suministrados al método mediante el objeto configurador.
             */
            miPipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement( PipeAdvertisement.getAdvertisementType());
            //Se establece el nombre de la tubería del servicio
            miPipeAdv.setName( config.getNombreTuberia() );
            //Se establece el tipo de tubería del servicio
            miPipeAdv.setType( config.getTipoTuberia() );						
            //Se crea un nuevo identificador para el anuncio de la tubería.
            miPipeAdv.setPipeID( (ID) IDFactory.newPipeID(miPeerGroup.getPeerGroupID()) );
            /*Se adjunta el anuncio de Tubería al de Especificación de Módulo*/
            miMSAdv.setPipeAdvertisement(miPipeAdv);
            
            /* Se publica el anuncio de Especificación de Módulo */
            miDiscoSvc.publish(miMSAdv);
            miDiscoSvc.remotePublish(miMSAdv, DiscoveryService.ADV);
            
        } catch (IOException ioe) {
            /* 
             * Ha ocurrido un error al inicializar el servicio. No se gestiona 
             * aquí el error, por lo que se lanza una ExcepcionP2P que 
             * contiene a la excepción causa del error.
             */
            throw new ExcepcionP2P("Error en la inicializacion del servicio", ioe);
        }											
    }
    
    /**
     * Devuelve una descripción del servicio prestado por este objeto.
     *
     * @return una cadena <code>String</code>  con la descripción de este
     *         servicio.
     */
    public String toString() {
        return "Implementacion de un servicio generico de JXTA";
    }
    
    /**
     * Devuelve el nombre de la tubería de un servicio especificado por
     * <code>nombreServicio</code> prestado por un peer con nombre dado por
     * <code>nombrePeer</code>. El objetivo de usar un cierto formato para 
     * los nombres de las tuberías de servicio es poder encontrar en el 
     * caché el anuncio de tubería de esa instancia del servicio en un 
     * determinado peer. Si no se hace esto, no se podría recuperar un anuncio
     * de servicio de un peer determinado desde el caché.
     *
     * @param  nombreServicio el nombre del servicio
     * @param  nombrePeer     el nombre del peer
     * @return                el nombre de la tubería del servicio especificado 
     *                        prestado por el peer especificado.
     */
    public static String nombreTuberiaServicio(String nombreServicio, 
                                               String nombrePeer) {
        return nombreServicio + "::" + nombrePeer;
    }
    
}