Guide des développeurs pour les services de données Sun Cluster 3.1 10/03

Création d'une application Java utilisant le protocole CRNP

L'exemple suivant illustre le processus de développement d'une application Java, CrnpClient, utilisant le protocole CRNP. Cette application enregistre les rappels d'événements avec le serveur CRNP sur le cluster, les écoute, puis traite les événements en imprimant leur contenu. Pour terminer, elle supprime les rappels d'événements.

Gardez ces informations à l'esprit pendant que vous étudiez cet exemple.

Configuration de l'environnement

Vous devez commencer par configurer votre environnement.

  1. Téléchargez puis installez JAXP ainsi que la version appropriée du compilateur Java et de la machine virtuelle.

    Vous trouvez de plus amples instructions à l'adresse suivante : http://java.sun.com/xml/jaxp/index.html.


    Remarque :

    cet exemple requiert Java 1.3.1 ou une version ultérieure.


  2. N'oubliez pas de spécifier un classpath sur la ligne de commande de compilation, afin que le compilateur puisse trouver les classes JAXP. Depuis le répertoire dans lequel figure le fichier source, entrez :


    % javac -classpath RACINE_JAXP/dom.jar:RACINE_JAXPjaxp-api. \
    jar:RACINE_JAXPsax.jar:RACINE_JAXPxalan.jar:RACINE_JAXP/xercesImpl \
    .jar:RACINE_JAXP/xsltc.jar -sourcepath . NOM_FICHIER_SOURCE.java
    

    RACINE_JAXP correspond au chemin d'accès, relatif ou absolu, au répertoire dans lequel se trouvent les fichiers jar de JAXP et NOM_FICHIER_SOURCE au nom du fichier Java source.

  3. Lors de l'exécution de l'application, spécifiez le classpath pour que l'application puisse charger les fichiers de classe JAXP appropriés (veuillez noter que le premier chemin d'accès spécifié dans le classpath correspond au répertoire courant) :


    java -cp .:RACINE_JAXP/dom.jar:RACINE_JAXPjaxp-api. \
    jar:RACINE_JAXPsax.jar:RACINE_JAXPxalan.jar:RACINE_JAXP/xercesImpl \
    .jar:RACINE_JAXP/xsltc.jar NOM_FICHIER_SOURCE ARGUMENTS
    

    L'environnement est à présent configuré. Vous pouvez développer l'application.

Premiers pas

Dans cette première partie de notre exemple, vous allez créer une classe de base appelée CrnpClient à l'aide d'une méthode principale qui analyse les arguments de la ligne de commande et crée un objet CrnpClient. Cet objet transfère les arguments de la ligne de commande à la classe, attend que l'utilisateur arrête l'application, exécute la commande shutdown sur la classe CrnpClient , puis se ferme.

Le constructeur de la classe CrnpClient doit exécuter les tâches suivantes :

    Créer le code Java mettant en oeuvre la logique précédente.

    L'exemple suivant présente le code du squelette de la classe CrnpClient. Les mises en oeuvre des quatre méthodes d'assistant référencées dans les méthodes d'arrêt et du constructeur sont décrites ultérieurement. Veuillez noter que le code important tous les packages requis est indiqué.


    import javax.xml.parsers.*;
    import javax.xml.transform.*;
    import javax.xml.transform.dom.*;
    import javax.xml.transform.stream.*;
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    import org.w3c.dom.*;
    
    import java.net.*;
    import java.io.*;
    import java.util.*;
    
    
    class CrnpClient
    {
            public static void main(String []args)
            {
                    InetAddress regIp = null;
                    int regPort = 0, localPort = 0;
    
                    try {
                            regIp = InetAddress.getByName(args[0]);
                            regPort = (new Integer(args[1])).intValue();
                            localPort = (new Integer(args[2])).intValue();
                    } catch (UnknownHostException e) {
                            System.out.println(e);
                            System.exit(1);
                    }
    
                    CrnpClient client = new CrnpClient(regIp, regPort, localPort,
                        args);
                    System.out.println("Hit return to terminate demo...");
                    try {
                            System.in.read();
                    } catch (IOException e) {
                            System.out.println(e.toString());
                    }
                    client.shutdown();
                    System.exit(0);
            }
    
            public CrnpClient(InetAddress regIpIn, int regPortIn, int localPortIn,
                String []clArgs)
            {
                    try {
                            regIp = regIpIn;
                            regPort = regPortIn;
                            localPort = localPortIn;
                            regs = clArgs;
    
                            setupXmlProcessing();
                            createEvtRecepThr();
                            registerCallbacks();
    
                    } catch (Exception e) {
                            System.out.println(e.toString());
                            System.exit(1);
                    }
            }
    
            public void shutdown()
            {
                    try {
                            unregister();
                    } catch (Exception e) {
                            System.out.println(e);
                            System.exit(1);
                    }
            }
    
            private InetAddress regIp;
            private int regPort;
            private EventReceptionThread evtThr;
            private String regs[];
    
            public int localPort;
            public DocumentBuilderFactory dbf;
    }

    Les variables membres sont traitées plus en détail par la suite.

Analyse des arguments de la ligne de commande

    Pour découvrir comment analyser les arguments de la ligne de commande, reportez-vous au code de l'Annexe G.

Définition d'un thread de réception d'événements

Vous devez vous assurer, au niveau du code, que les événements sont reçus sur un thread distinct, afin que votre application puisse continuer d'exécuter d'autres tâches tandis que le thread d'événements attend la notification de rappel d'événements.


Remarque :

la configuration XML est traitée ultérieurement.


  1. Dans votre code, définissez une sous-classe Thread appelée EventReceptionThread créant une classe ServerSocket et attendant que le socket reçoive des événements.

    Dans cette partie du code, les événements ne sont ni lus ni traités. La lecture et le traitement des événements sont abordés ultérieurement. La classe EventReceptionThread crée une classe ServerSocket sur l'adresse générique du protocole interréseau. EventReceptionThread conserve également l'objet CrnpClient en référence, afin de pouvoir lui transmettre des événements.


    class EventReceptionThread extends Thread
    {
            public EventReceptionThread(CrnpClient clientIn) throws IOException
            {
                    client = clientIn;
                    listeningSock = new ServerSocket(client.localPort, 50,
                        InetAddress.getLocalHost());
            }
    
            public void run()
            {
                    try {
                            DocumentBuilder db = client.dbf.newDocumentBuilder();
                            db.setErrorHandler(new DefaultHandler());
    
                            while(true) {
                                    Socket sock = listeningSock.accept();
                                    // Créer l'événement à partir du flux du sock et le traiter
                                    sock.close();
                            }
                            // IMPOSSIBLE À ATTEINDRE
    
                    } catch (Exception e) {
                            System.out.println(e);
                            System.exit(1);
                    }
            }
    
            /* private member variables */
            private ServerSocket listeningSock;
            private CrnpClient client;
    }

  2. Maintenant que vous savez comment fonctionne la classe EventReceptionThread, créez un objet createEvtRecepThr :


    private void createEvtRecepThr() throws Exception
    {
            evtThr = new EventReceptionThread(this);
            evtThr.start();
    }

Connexion et déconnexion des rappels

La connexion recoupe les tâches suivantes :

  1. Créez le code Java mettant en oeuvre la logique précédente.

    L'exemple suivant présente la mise en oeuvre de la méthode registerCallbacks de la classe CrnpClient (qui est appelée par le constructeur CrnpClient). Les appels des fonctions createRegistrationString() et readRegistrationReply() sont décrits ultérieurement.

    regIp et regPort sont des objets membres définis par le constructeur.


    private void registerCallbacks() throws Exception
    {
            Socket sock = new Socket(regIp, regPort);
            String xmlStr = createRegistrationString();
            PrintStream ps = new
                    PrintStream(sock.getOutputStream());
            ps.print(xmlStr);
            readRegistrationReply(sock.getInputStream();
            sock.close();
    }

  2. Mettez en oeuvre la méthode unregister. Cette méthode est appelée par la méthode shutdown de la classe CrnpClient. La mise en oeuvre de createUnregistrationString est présentée plus en détail ultérieurement.


    private void unregister() throws Exception
    {
            Socket sock = new Socket(regIp, regPort);
            String xmlStr = createUnregistrationString();
            PrintStream ps = new PrintStream(sock.getOutputStream());
            ps.print(xmlStr);
            readRegistrationReply(sock.getInputStream());
            sock.close();
    }

Génération du langage XML

Maintenant que vous avez défini la structure de l'application et rédigé l'intégralité du code de gestion de réseaux, vous rédigez le code qui génère et analyse XML. Commencez par rédiger le code générant le message de connexion XML SC_CALLBACK_REG.

Un message SC_CALLBACK_REG est constitué d'un type de connexion (ADD_CLIENT, REMOVE_CLIENT, ADD_EVENTS ou REMOVE_EVENTS), d'un port de rappel et d'une liste d'événements intéressants. Chaque événement comprend une classe et une sous-classe, suivies d'une liste de paires nom/valeurs.

Dans cette partie de l'exemple, vous rédigez une classe CallbackReg qui enregistre le type de connexion, le port de rappel et la liste des événements de connexion. Cette classe peut également se sérialiser en message XML SC_CALLBACK_REG.

Cette classe comprend la méthode convertToXml. Cette méthode intéressante crée une chaîne de message XML SC_CALLBACK_REG à partir des membres de la classe. La documentation JAXP disponible à l'adresse suivante http://java.sun.com/xml/jaxp/index.html présente plus en détail le code de cette méthode.

La mise en oeuvre de la classe Event est présentée ci-après. Veuillez noter que la classe CallbackReg utilise une classe Event qui mémorise un événement et peut le convertir en Element XML.

  1. Créez le code Java mettant en oeuvre la logique précédente.


    class CallbackReg
    {
            public static final int ADD_CLIENT = 0;
            public static final int ADD_EVENTS = 1;
            public static final int REMOVE_EVENTS = 2;
            public static final int REMOVE_CLIENT = 3;
    
            public CallbackReg()
            {
                    port = null;
                    regType = null;
                    regEvents = new Vector();
            }
    
            public void setPort(String portIn)
            {
                    port = portIn;
            }
    
            public void setRegType(int regTypeIn)
            {
                    switch (regTypeIn) {
                    case ADD_CLIENT:
                            regType = "ADD_CLIENT";
                            break;
                    case ADD_EVENTS:
                            regType = "ADD_EVENTS";
                            break;
                    case REMOVE_CLIENT:
                            regType = "REMOVE_CLIENT";
                            break;
                    case REMOVE_EVENTS:
                            regType = "REMOVE_EVENTS";
                            break;
                    default:
                            System.out.println("Error, invalid regType " +
                                regTypeIn);
                            regType = "ADD_CLIENT";
                            break;
                    }
            }
    
            public void addRegEvent(Event regEvent)
            {
                    regEvents.add(regEvent);
            }
    
            public String convertToXml()
            {
                    Document document = null;
                    DocumentBuilderFactory factory =
                        DocumentBuilderFactory.newInstance();
                    try {
                            DocumentBuilder builder = factory.newDocumentBuilder();
                            document = builder.newDocument();
                    } catch (ParserConfigurationException pce) {
                            // L'analyseur avec options spécifiques ne peut être créé
                            pce.printStackTrace();
                            System.exit(1);
                    }
    
                    // Créer l'élément racine
                    Element root = (Element) document.createElement(
                        "SC_CALLBACK_REG");
    
                    // Ajouter les attributs
                    root.setAttribute("VERSION", "1.0");
                    root.setAttribute("PORT", port);
                    root.setAttribute("regType", regType);
    
                    // Ajouter les événements
                    for (int i = 0; i < regEvents.size(); i++) {
                            Event tempEvent = (Event)
                                (regEvents.elementAt(i));
                            root.appendChild(tempEvent.createXmlElement(
                                document));
                    }
                    document.appendChild(root);
    
                    // Tout convertir en chaîne
                    DOMSource domSource = new DOMSource(document);
                    StringWriter strWrite = new StringWriter();
                    StreamResult streamResult = new StreamResult(strWrite);
                    TransformerFactory tf = TransformerFactory.newInstance();
                    try {
                            Transformer transformer = tf.newTransformer();
                            transformer.transform(domSource, streamResult);
                    } catch (TransformerException e) {
                            System.out.println(e.toString());
                            return ("");
                    }
                    return (strWrite.toString());
            }
    
            private String port;
            private String regType;
            private Vector regEvents;
    }

  2. Mettez en oeuvre les classes Event et NVPair.

    Veuillez noter que la classe CallbackReg utilise une classe Event qui utilise elle-même une classe NVPair.


    class Event
    {
    
            public Event()
            {
                    regClass = regSubclass = null;
                    nvpairs = new Vector();
            }
    
            public void setClass(String classIn)
            {
                    regClass = classIn;
            }
    
            public void setSubclass(String subclassIn)
            {
                    regSubclass = subclassIn;
            }
    
            public void addNvpair(NVPair nvpair)
            {
                    nvpairs.add(nvpair);
            }
    
            public Element createXmlElement(Document doc)
            {
                    Element event = (Element)
                        doc.createElement("SC_EVENT_REG");
                    event.setAttribute("CLASS", regClass);
                    if (regSubclass != null) {
                            event.setAttribute("SUBCLASS", regSubclass);
                    }
                    for (int i = 0; i < nvpairs.size(); i++) {
                              NVPair tempNv = (NVPair)
                                  (nvpairs.elementAt(i));
                              event.appendChild(tempNv.createXmlElement(
                                  doc));
                    }
                    return (event);
            }
    
            private String regClass, regSubclass;
            private Vector nvpairs;
    }
    
    class NVPair
    {
            public NVPair()
            {
                    name = value = null;
            }
    
            public void setName(String nameIn)
            {
                    name = nameIn;
            }
    
            public void setValue(String valueIn)
            {
                    value = valueIn;
            }
    
            public Element createXmlElement(Document doc)
            {
                    Element nvpair = (Element)
                        doc.createElement("NVPAIR");
                    Element eName = doc.createElement("NAME");
                    Node nameData = doc.createCDATASection(name);
                    eName.appendChild(nameData);
                    nvpair.appendChild(eName);
                    Element eValue = doc.createElement("VALUE");
                    Node valueData = doc.createCDATASection(value);
                    eValue.appendChild(valueData);
                    nvpair.appendChild(eValue);
    
                    return (nvpair);
            }
    
            private String name, value;
    }

Création des messages de connexion et de déconnexion

Maintenant que vous avez créé les classes d'assistant générant les messages XML, vous pouvez écrire la mise en oeuvre de la méthode createRegistrationString. Cette méthode est appelée par la méthode registerCallbacks présentée dans la rubrique Connexion et déconnexion des rappels.

createRegistrationString crée un objet CallbackReg et définit son type et son port de connexion. createRegistrationString crée divers événements à l'aide des méthodes d'assistant createAllEvent, createMembershipEvent, createRgEvent et createREvent. Chaque événement est ajouté à l'objet CallbackReg une fois cet objet créé. Enfin, createRegistrationString appelle la méthode convertToXml sur l'objet CallbackReg pour rechercher et extraire les messages XML au format String.

Veuillez noter que la variable membre regs enregistre les arguments de la ligne de commande qu'un utilisateur fournit à l'application. Le cinquième argument et les arguments ultérieurs spécifient les événements pour lesquels l'application doit se connecter. Le quatrième argument spécifie le type de connexion mais il est ignoré dans cet exemple. Le code complet présenté dans l'Annexe G montre comment utiliser ce quatrième argument.

  1. Créez le code Java mettant en oeuvre la logique précédente.


    private String createRegistrationString() throws Exception
    {
            CallbackReg cbReg = new CallbackReg();
            cbReg.setPort("" + localPort);
    
            cbReg.setRegType(CallbackReg.ADD_CLIENT);
    
            // ajouter les événements
            for (int i = 4; i < regs.length; i++) {
                    if (regs[i].equals("M")) {
                            cbReg.addRegEvent(
                                createMembershipEvent());
                    } else if (regs[i].equals("A")) {
                            cbReg.addRegEvent(
                                createAllEvent());
                    } else if (regs[i].substring(0,2).equals("RG")) {
                            cbReg.addRegEvent(createRgEvent(
                                regs[i].substring(3)));
                    } else if (regs[i].substring(0,1).equals("R")) {
                            cbReg.addRegEvent(createREvent(
                                regs[i].substring(2)));
                    }
            }
    
            String xmlStr = cbReg.convertToXml();
            return (xmlStr);
    }
    
    private Event createAllEvent()
    {
            Event allEvent = new Event();
            allEvent.setClass("EC_Cluster");
            return (allEvent);
    }
    
    private Event createMembershipEvent()
    {
            Event membershipEvent = new Event();
            membershipEvent.setClass("EC_Cluster");
            membershipEvent.setSubclass("ESC_cluster_membership");
            return (membershipEvent);
    }
    
    private Event createRgEvent(String rgname)
    {
            Event rgStateEvent = new Event();
            rgStateEvent.setClass("EC_Cluster");
            rgStateEvent.setSubclass("ESC_cluster_rg_state");
    
            NVPair rgNvpair = new NVPair();
            rgNvpair.setName("rg_name");
            rgNvpair.setValue(rgname);
            rgStateEvent.addNvpair(rgNvpair);
    
            return (rgStateEvent);
    }
    
    private Event createREvent(String rname)
    {
            Event rStateEvent = new Event();
            rStateEvent.setClass("EC_Cluster");
            rStateEvent.setSubclass("ESC_cluster_r_state");
    
            NVPair rNvpair = new NVPair();
            rNvpair.setName("r_name");
            rNvpair.setValue(rname);
            rStateEvent.addNvpair(rNvpair);
    
            return (rStateEvent);
    }

  2. Créez la chaîne de caractères de déconnexion.

    La création de la chaîne de caractères de déconnexion est plus facile à créer que la chaîne de caractères de connexion car vous n'avez pas à prendre en charge les événements :


    private String createUnregistrationString() throws Exception
    {
            CallbackReg cbReg = new CallbackReg();
            cbReg.setPort("" + localPort);
            cbReg.setRegType(CallbackReg.REMOVE_CLIENT);
            String xmlStr = cbReg.convertToXml();
            return (xmlStr);
    }

Configuration de l'analyseur XML

Maintenant que vous avez créé le code de génération XML et de gestion de réseaux de l'application, l'étape finale consiste à analyser et traiter la réponse de connexion et les rappels d'événement. Le constructeur CrnpClient appelle une méthode setupXmlProcessing. Cette méthode crée un objet DocumentBuilderFactory et définit plusieurs propriétés d'analyse sur cet objet. La documentation JAXP disponible à l'adresse suivante http://java.sun.com/xml/jaxp/index.html présente plus en détail cette méthode.

    Créez le code Java mettant en oeuvre la logique précédente.


    private void setupXmlProcessing() throws Exception
    {
            dbf = DocumentBuilderFactory.newInstance();
    
            // Nous n'avons pas besoin de valider
            dbf.setValidating(false);
            dbf.setExpandEntityReferences(false);
    
            // Nous souhaitons ignorer les commentaires et les espaces
            dbf.setIgnoringComments(true);
            dbf.setIgnoringElementContentWhitespace(true);
    
            // Combiner les sections CDATA en noeuds TEXT.
            dbf.setCoalescing(true);
    }

Analyse de la réponse de connexion

Pour analyser le message XML SC_REPLY que le serveur CRNP transmet en réponse à un message de connexion ou de déconnexion, vous avez besoin d'une classe d'assistant RegReply. Vous pouvez créer cette classe à partir d'un document XML. Elle propose les mécanismes d'accès du code et du message d'état. Pour analyser le flux XML du serveur, vous devez créer un nouveau document XML et utiliser cette méthode d'analyse (la documentation JAXP disponible à l'adresse suivante http://java.sun.com/xml/jaxp/index.html présente plus en détail cette méthode).

  1. Créez le code Java mettant en oeuvre la logique précédente.

    Veuillez noter que la méthode readRegistrationReply utilise la nouvelle classe RegReply.


    private void readRegistrationReply(InputStream stream) throws Exception
    {
            // Créer le constructeur de document
            DocumentBuilder db = dbf.newDocumentBuilder();
            db.setErrorHandler(new DefaultHandler());
    
            //analyser le fichier d'entrée
            Document doc = db.parse(stream);
    
            RegReply reply = new RegReply(doc);
            reply.print(System.out);
    }

  2. Mettez en oeuvre la classe RegReply.

    Veuillez noter que la méthode retrieveValues parcourt l'arborescence DOM dans le document XML et extrait le code et le message d'état. La documentation JAXP disponible à l'adresse suivante http://java.sun.com/xml/jaxp/index.html contient de plus amples informations.


    class RegReply
    {
            public RegReply(Document doc)
            {
                    retrieveValues(doc);
            }
    
            public String getStatusCode()
            {
                    return (statusCode);
            }
    
            public String getStatusMsg()
            {
                    return (statusMsg);
            }
            public void print(PrintStream out)
            {
                    out.println(statusCode + ": " +
                        (statusMsg != null ? statusMsg : ""));
            }
    
            private void retrieveValues(Document doc)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Trouver l'élément SC_REPLY.
                    nl = doc.getElementsByTagName("SC_REPLY");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "SC_REPLY node.");
                            return;
                    }
    
                    n = nl.item(0);
    
                    // Récupérer la valeur de l'attribut statusCode
                    statusCode = ((Element)n).getAttribute("STATUS_CODE");
    
                    // Trouver l'élément SC_STATUS_MSG
                    nl = ((Element)n).getElementsByTagName("SC_STATUS_MSG");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "SC_STATUS_MSG node.");
                            return;
                    }
                    // Obtenir la section TEXT, le cas échéant.
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                            // Si elle n'existe pas, ce n'est pas une erreur, retourner
                            sans avertissement.
                            return;
                    }
    
                    // Récupérer la valeur
                    statusMsg = n.getNodeValue();
            }
    
            private String statusCode;
            private String statusMsg;
    }

Analyse des événements de rappel

L'étape finale consiste à analyser et traiter les événements de rappels réels. Pour faciliter cette tâche, modifiez la classe Event que vous avez créée à la rubrique Génération du langage XML, afin qu'elle puisse créer un Event à partir d'un document XML et un Element XML. Cette modification requiert un constructeur supplémentaire (exécutant un document XML), une méthode retrieveValues, l'ajout de deux variables membres (vendor et publisher), des méthodes de mécanisme d'accès à tous les champs et une méthode d'impression.

  1. Créez le code Java mettant en oeuvre la logique précédente.

    Veuillez noter que ce code est identique à celui de la classe RegReply décrite dans la rubrique Analyse de la réponse de connexion.


            public Event(Document doc)
            {
                    nvpairs = new Vector();
                    retrieveValues(doc);
            }
            public void print(PrintStream out)
            {
                    out.println("\tCLASS=" + regClass);
                    out.println("\tSUBCLASS=" + regSubclass);
                    out.println("\tVENDOR=" + vendor);
                    out.println("\tPUBLISHER=" + publisher);
                    for (int i = 0; i < nvpairs.size(); i++) {
                            NVPair tempNv = (NVPair)
                                (nvpairs.elementAt(i));
                            out.print("\t\t");
                            tempNv.print(out);
                    }
            }
    
            private void retrieveValues(Document doc)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Trouver l'élément SC_EVENT.
                    nl = doc.getElementsByTagName("SC_EVENT");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "SC_EVENT node.");
                            return;
                    }
    
                    n = nl.item(0);
    
                    //
                    // Récupérer la valeurs des attributs de CLASS, SUBCLASS,
                    // VENDOR et PUBLISHER.
                    //
                    regClass = ((Element)n).getAttribute("CLASS");
                    regSubclass = ((Element)n).getAttribute("SUBCLASS");
                    publisher = ((Element)n).getAttribute("PUBLISHER");
                    vendor = ((Element)n).getAttribute("VENDOR");
    
                    // Récupérer toutes les paires nv
                    for (Node child = n.getFirstChild(); child != null;
                        child = child.getNextSibling())
                    {
                           nvpairs.add(new NVPair((Element)child));
                    }
            }
    
            public String getRegClass()
            {
                    return (regClass);
            }
    
            public String getSubclass()
            {
                    return (regSubclass);
            }
    
            public String getVendor()
            {
                    return (vendor);
            }
    
            public String getPublisher()
            {
                    return (publisher);
            }
    
            public Vector getNvpairs()
            {
                    return (nvpairs);
            }
    
            private String vendor, publisher;

  2. Mettez en oeuvre les autres constructeurs et méthodes de la classe NVPair qui prennent en charge l'analyse XML.

    Les modifications apportées à la classe Event (voir Étape 1) doivent être identiques aux modifications apportées à la classe NVPair.


            public NVPair(Element elem)
            {
                    retrieveValues(elem);
            }
            public void print(PrintStream out)
            {
                    out.println("NAME=" + name + " VALUE=" + value);
            }
            private void retrieveValues(Element elem)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Trouver l'élément NAME
                    nl = elem.getElementsByTagName("NAME");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "NAME node.");
                            return;
                    }
                    // Obtenir la section TEXT
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                            System.out.println("Error in parsing: can't find "
                                + "TEXT section.");
                            return;
                    }
    
                    // Récupérer la valeur
                    name = n.getNodeValue();
    
                    // Obtenir maintenant l'élément de valeur
                    nl = elem.getElementsByTagName("VALUE");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "VALUE node.");
                            return;
                    }
                    // Obtenir la section TEXT
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                    System.out.println("Error in parsing: can't find "
                                + "TEXT section.");
                            return;
                    }
    
                    // Récupérer la valeur
                    value = n.getNodeValue();
                    }
    
            public String getName()
            {
                    return (name);
            }
    
            public String getValue()
            {
                    return (value);
            }
    }

  3. Mettez en oeuvre une boucle while dans la classe EventReceptionThread attendant les rappels d'événements (EventReceptionThread est présenté dans la rubrique Définition d'un thread de réception d'événements).


    while(true) {
                    Socket sock = listeningSock.accept();
                    Document doc = db.parse(sock.getInputStream());
                    Event event = new Event(doc);
                    client.processEvent(event);
                    sock.close();
            }

Exécution de l'application

    Exécutez votre application.


    # java CrnpClient hôte_crnp port_crnp port_local ...
    

    Le code complet de l'application CrnpClient figure à l'Annexe G.