/*
 * Fichero:     GrupoDePeers.java
 * Fecha:       20/10/06
 * Autor:       Alvaro Sarmiento Losada
 * Asignatura:  Proyecto Fin de Carrera
 * Descripción: Contiene la clase GrupoDePeers, que representa a un grupo
 *              de peers JXTA que han acordado el uso de un determinado
 *              conjunto de servicios JXTA.
 *
 * 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.discovery.DiscoveryListener;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.ModuleSpecAdvertisement;
import net.jxta.protocol.PeerAdvertisement;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.discovery.DiscoveryEvent;

import java.util.Enumeration;

/**
 * Esta clase representa a un grupo de peers JXTA que comparten un conjunto
 * de servicios JXTA comunes. Esta clase proporciona un método de búsqueda 
 * de dichos servicios, dejando la invocación de los servicios a las 
 * aplicaciones.
 *
 * @author  Alvaro Sarmiento Losada
 */
public class GrupoDePeers extends GrupoDePeersGenerico 
                       implements DiscoveryListener {
    
    /** 
     * Almacena un anuncio de tubería JXTA encontrado durante la búsqueda
     * del servicio.
     */
    private PipeAdvertisement anuncioTuberia = null;

    /** 
     * Almacena el nombre de tubería JXTA del anuncio encontrado durante 
     * la búsqueda del servicio.
     */
    private String nombreTuberia = null;
    
    /**
     * Crea una nueva instancia de esta clase, dado el grupo de peers al que
     * este objeto representará.
     *
     * @param grupo el <code>PeerGroup</code> que representará este objeto.
     */
    public GrupoDePeers(PeerGroup grupo) {
        super(grupo);
    }
    
    /**
     * Realiza una búsqueda de un servicio determinado por
     * <code>nombreServicio</code> implementado en un peer especificado por
     * <code>anuncioPeer</code>, devolviendo el anuncio de tubería válido
     * para conectarse a dicho servicio.
     *
     * @param  nombreServicio el nombre del servicio buscado.
     * @param  anuncioPeer    el anuncio  <code>PeerAdvertisement</code> 
     *                        del peer donde se quiere encontrar el servicio.
     * @return                el anuncio de tubería del servicio implementado
     *                        por el peer especificado, con el que se puede 
     *                        invocar el servicio. <code>null</code> si el peer
     *                        especificado no posee ese servicio o no ha
     *                        respondido a la búsqueda.
     */
    public PipeAdvertisement encontrarServicioDeUnPeer(String nombreServicio, 
                                            PeerAdvertisement anuncioPeer) {
        
        /*
         * Se modifica el nombre de tubería, añadiendo el nombre del peer. 
         * Se hace para poder hacer búsquedas en caché de un anuncio
         * de un peer determinado. ya que en los anuncios de servicio no se
         * especifica en ningún momento qué peer es el que ofrece el servicio.
         * Esto es como respuesta a un "defecto" de JXTA, ya que los métodos
         * de búsqueda de anuncios en caché no permiten buscar anuncios de un
         * peer determinado. 
         * Por esta razón, en la clase ConfiguradorServicioP2P se establece
         * también el nombre de la tubería con el nombre del peer, y se publica
         * de esta forma. Sin embargo, el servicio es el mismo para todos los
         * peers, sólo cambia el nombre de la tubería.
         */
        nombreTuberia = ServicioGenericoP2P.nombreTuberiaServicio(nombreServicio, anuncioPeer.getName());
        System.out.println("nombre tuberia:"+nombreTuberia);
        //Se inicializa a null para borrar el antiguo valor de pasadas búsquedas
        anuncioTuberia = null;

        /* Se busca primero en cache */
        Enumeration enumerador = descubrirDelCache(DiscoveryService.ADV, 
                                             "Name", 
                                             nombreServicio); /*nombreServicio*/
        if ( enumerador != null ) {
            /*
             * Se han encontrado anuncios que responden al buscado. Se 
             * comprueba que el anuncio de tubería sea el buscado realmente,
             * comparando los nombres de la tubería.
             */
            while ( (anuncioTuberia == null) && (enumerador.hasMoreElements() == true) ) {
                try {
                    //Se obtiene el anuncio de tubería.
                    ModuleSpecAdvertisement msAdv = 
                                   (ModuleSpecAdvertisement) enumerador.nextElement();
                    PipeAdvertisement aux = msAdv.getPipeAdvertisement();
                    /*
                     * Si los nombres de la tubería coinciden, es el 
                     * anuncio buscado.
                     */
                    if ( aux.getName().equals(nombreTuberia) == true ) {
                        //Es el anuncio buscado, y ya no se itera más.
                        anuncioTuberia = aux;
                    }
                } catch (Throwable t) {  
                    /*
                     * Es un error en tiempo de ejecución porque ha habido una
                     * excepción al hacer el cast. No se procesa nada, se sigue
                     * iterando.
                     */
                }
            }
        }

        if ( anuncioTuberia == null ){
            /* No se ha encontrado en cache, buscamos por la red */            
            setIntervaloBusqueda(INTENTOS, TIEMPO);
            /*
             * Al recibir una respuesta de búsqueda, se invoca al método
             * discoveryEvent() que se encargará de verificar que la
             * respuesta es la buscada. Si encuentra el anuncio, lo devuelve
             * mediante la variable privada anuncioTuberia.
             */
            descubrir(anuncioPeer.getPeerID().toString(), 
                      DiscoveryService.ADV, 
                      "Name", 
                      nombreServicio, 
                      this);
        }
        
        return anuncioTuberia;
    }
    
    public void discoveryEvent(DiscoveryEvent evento) {
        //Se obtiene la lista de anuncios recibidos
        Enumeration enumerador = evento.getSearchResults();
        if ( enumerador != null ) {
            //Se busca en la lista hasta encontrar el anuncio deseado
            while (    (anuncioTuberia == null) 
                    && (enumerador.hasMoreElements() == true) ) {
                try {
                    //Se obtiene el anuncio de tubería
                    ModuleSpecAdvertisement msAdv 
                                = (ModuleSpecAdvertisement) enumerador.nextElement();
                    PipeAdvertisement aux = msAdv.getPipeAdvertisement();
                    /*
                     * Si los nombres de la tubería coinciden, es el 
                     * anuncio buscado y no se itera más.
                     */
                    if ( aux.getName().equals(nombreTuberia) == true ) {
                        //Se encontró el anuncio buscado, ya no se itera más
                        anuncioTuberia = aux;
                    }
                } catch (Throwable t) {
                    /*
                     * Es un error en tiempo de ejecución porque ha habido una
                     * excepción al hacer el cast. No se procesa nada, se sigue
                     * iterando.
                     */
                }
            }
        }
        /* 
         * No se devuelve nada, sino que se pasa en la variable privada
         * anuncioTuberia.
         */
    }

}