Este apéndice muestra la aplicación CrnpClient.java completa, que se explica con más detalle en el Capítulo 12.
/* * CrnpClient.java * ================ * * Nota sobre el análisis de XML: * * Este programa usa la arquitectura Java de Sun para las API de procesos de XML (JAXP). * Consulte http://java.sun.com/xml/jaxp/index.html para ver documentación sobre API y * obtener información sobre la disponibilidad. * * Este programa se ha escrito para Java 1.3.1 o superior. * * Información general del programa: * * El subproceso principal del programa crea un objeto CrnpClient, espera que el * usuario termine la demostración e invoca el cierre en el objeto CrnpClient * y sale del programa. * * El constructor de CrnpClient crea un objeto EventReceptionThread, abre una * conexión con el servidor CRNP (con el sistema y puerto especificados en la líne * a de órdenes), construye un mensaje de registro (basado en las especificaciones * de la línea de órdenes), envía el mensaje de registro y lee y analiza la respuesta. * * EventReceptionThread crea un zócalo de recepción vinculado al nombre de sistema * de la máquina en la que se ejecuta el programa y el puerto especificado en la línea * de órdenes. Espera una rellamada de evento entrante y, cuando la recibe, construye * un documento XML desde el canal del zócalo de entrada, que se devuelve al objeto * CrnpClient para ser procesado. * * El método de apagado de CrnpClient se limita a enviar un mensaje de cancelación * de registro (REMOVE_CLIENT) SC_CALLBACK_REG al servidor crnp. * * Nota sobre el manejo de errores: en pro de la brevedad, este programa suele salir * cuando se producen la mayoría de los errores. Obviamente, una aplicación real * intentaría manejar algunos errores de formas diversas, por ejemplo, mediante * reintentos cuando corresponda. */ // paquetes JAXP 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.*; // paquetes estándar import java.net.*; import java.io.*; import java.util.*; /* * clase CrnpClient * ----------------- * Consulte los comentarios de cabecera de archivos anteriores. */ class CrnpClient { /* * main * ---- * El punto de entrada de la ejecución main, sólo verifica * el número de argumentos de la línea de órdenes y construye * una instancia de CrnpClient para que realice todo el trabajo. */ public static void main(String []args) { InetAddress regIp = null; int regPort = 0, localPort = 0; /* Verificar el número de argumentos de la línea de órdenes */ if (args.length < 4) { System.out.println( "Sintaxis: java CrnpClient sistemacrnp puertocrnp " + "puertolocal (-ac | -ae | -re) " + "[(M | A | RG=nombre | R=nombre) [...]]"); System.exit(1); } /* * Se espera que la línea de órdenes contenga la IP/el puerto del * servidor crnp, el puerto local en el que se debe recibir y * argumentos que especifiquen el tipo de registro. */ 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); } // Crear CrnpClient CrnpClient client = new CrnpClient(regIp, regPort, localPort, args); // Esperar hasta que el usuario desee terminar el programa System.out.println("Pulsar Retorno para terminar la demostración..."); // la lectura se bloquea hasta que el usuario escriba algo try { System.in.read(); } catch (IOException e) { System.out.println(e.toString()); } // apagar el cliente client.shutdown(); System.exit(0); } /* * ====================== * métodos públicos * ====================== */ /* * constructor CrnpClient * ----------------------- * Analiza los argumentos de la línea de órdenes para saber cómo contactar con * el servidor crnp, crea el subproceso de recepción de eventos y lo pone * en ejecución, crea el objeto XML DocumentBuilderFactory y, finalmente, se * registra para recibir rellamadas en el servidor crnp. */ public CrnpClient(InetAddress regIpIn, int regPortIn, int localPortIn, String []clArgs) { try { regIp = regIpIn; regPort = regPortIn; localPort = localPortIn; regs = clArgs; /* * Configurar el creador de documentos para * el proceso de xml. */ setupXmlProcessing(); /* * Crear EventReceptionThread, que crea * ServerSocket y lo vincula a un puerto e ip local. */ createEvtRecepThr(); /* * Registrar en el servidor crnp. */ registerCallbacks(); } catch (Exception e) { System.out.println(e.toString()); System.exit(1); } } /* * processEvent * --------------- * Rellamada a CrnpClient, utilizada por EventReceptionThread * cuando recibe rellamadas de eventos. */ public void processEvent(Event event) { /* * Con fines demostrativos, imprima el evento en System.out. * Por supuesto, una aplicación real utilizaría el evento * de algún modo. */ event.print(System.out); } /* * apagado * -------- * Cancelar el registro del servidor CRNP. */ public void shutdown() { try { /* Enviar un mensaje de cancelación de registro al servidor */ unregister(); } catch (Exception e) { System.out.println(e); System.exit(1); } } /* * ====================== * métodos de ayuda privados * ====================== */ /* * setupXmlProcessing * -------------------- * Crear el generador de documentos para * analizar los eventos y respuestas de xml. */ private void setupXmlProcessing() throws Exception { dbf = DocumentBuilderFactory.newInstance(); // No es necesario validar dbf.setValidating(false); dbf.setExpandEntityReferences(false); // Se desea ignorar comentarios y espacios en blanco dbf.setIgnoringComments(true); dbf.setIgnoringElementContentWhitespace(true); // Fusionar secciones CDATA en nodos de TEXT. dbf.setCoalescing(true); } /* * createEvtRecepThr * ------------------- * Crea un objeto EventReceptionThread nuevo, guarda el ip * y puerto al que está vinculado el zócalo receptor y * pone el subproceso en ejecución. */ private void createEvtRecepThr() throws Exception { /* crear el objeto de subproceso */ evtThr = new EventReceptionThread(this); /* * Ahora, iniciar la ejecución del subproceso para empezar a * recibir rellamadas de entrega de eventos. */ evtThr.start(); } /* * registerCallbacks * ------------------ * Crea una conexión de zócalo al servidor crnp y envía * un mensaje de registro de evento. */ private void registerCallbacks() throws Exception { System.out.println("Acerca del registro"); /* * Crear un zócalo conectado al ip/puerto de registro * del servidor crnp y enviar la información de registro. */ Socket sock = new Socket(regIp, regPort); String xmlStr = createRegistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); /* * Leer la respuesta */ readRegistrationReply(sock.getInputStream()); /* * Cerrar la conexión de zócalo. */ sock.close(); } /* * unregister * ---------- * Al igual que en registerCallbacks, crear una conexión de zócalo al * servidor crnp, enviar un mensaje de cancelación de registro, esperar * la respuesta del servidor y cerrar el zócalo. */ private void unregister() throws Exception { System.out.println("Acerca de la cancelación del registro"); /* * Crear un zócalo conectado al ip/puerto de registro del * servidor crnp y enviar información de cancelación de registro . */ Socket sock = new Socket(regIp, regPort); String xmlStr = createUnregistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); /* * Leer la respuesta */ readRegistrationReply(sock.getInputStream()); /* * Cerrar la conexión de zócalo. */ sock.close(); } /* * createRegistrationString * ------------------ * Construye un objeto CallbackReg basado en los argumentos de la línea de * órdenes de este programa; después, recupera la cadena XML del objeto * CallbackReg. */ private String createRegistrationString() throws Exception { /* * Crear la clase real de CallbackReg y establecer el puerto. */ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); // Establecer el tipo de registro if (regs[3].equals("-ac")) { cbReg.setRegType(CallbackReg.ADD_CLIENT); } else if (regs[3].equals("-ae")) { cbReg.setRegType(CallbackReg.ADD_EVENTS); } else if (regs[3].equals("-re")) { cbReg.setRegType(CallbackReg.REMOVE_EVENTS); } else { System.out.println("Tipo de registro no válido: " + regs[3]); System.exit(1); } // Agregar los eventos 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(); System.out.println(xmlStr); return (xmlStr); } /* * createAllEvent * ---------------- * Crea un evento de registro de XML con clase EC_Cluster, y sin * subclase. */ private Event createAllEvent() { Event allEvent = new Event(); allEvent.setClass("EC_Cluster"); return (allEvent); } /* * createMembershipEvent * ---------------------- * Crea un evento de registro de XML con EC_Cluster, subclase * ESC_cluster_membership. */ private Event createMembershipEvent() { Event membershipEvent = new Event(); membershipEvent.setClass("EC_Cluster"); membershipEvent.setSubclass("ESC_cluster_membership"); return (membershipEvent); } /* * createRgEvent * ---------------- * Crea un evento de registro de XML con clase EC_Cluster, * subclase ESC_cluster_rg_state y un nvpair "rg_name" (basado * en parámetro de entrada). */ private Event createRgEvent(String rgname) { /* * Crea un evento de cambio de estado de grupo de recursos para el * grupo de recursos nombre_registro. Observar que se da * un par nombre-valor (nvpair) para este tipo de evento para * especificar el grupo de recursos que interesa. */ /* * Construir el objeto de evento y fijar la clase y subclase. */ Event rgStateEvent = new Event(); rgStateEvent.setClass("EC_Cluster"); rgStateEvent.setSubclass("ESC_cluster_rg_state"); /* * Crear el objeto nvpair y agregarlo al evento. */ NVPair rgNvpair = new NVPair(); rgNvpair.setName("rg_name"); rgNvpair.setValue(rgname); rgStateEvent.addNvpair(rgNvpair); return (rgStateEvent); } /* * createREvent * ---------------- * Crea un evento de registro de XML con clase EC_Cluster, * subclase ESC_cluster_r_state y un nvpair "r_name" (basado * en parámetro de entrada). */ private Event createREvent(String rname) { /* * Crea un evento de cambio de estado de recurso para el * recurso nombre_registro. Observar que se indica un par * nombre-valor (nvpair) para este tipo de evento, para * especificar el grupo de recursos que interesa. */ 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); } /* * createUnregistrationString * ------------------ * Construye un objeto REMOVE_CLIENT CallbackReg, después recupera * la cadena XML del objeto CallbackReg. */ private String createUnregistrationString() throws Exception { /* * Crear objeto CallbackReg */ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); cbReg.setRegType(CallbackReg.REMOVE_CLIENT); /* * Se dirige el registro hacia OutputStream */ String xmlStr = cbReg.convertToXml(); // Imprimir la cadena para la depuración System.out.println(xmlStr); return (xmlStr); } /* * readRegistrationReply * ------------------------ * Analizar el xml en un documento, construir un objeto RegReply * a partir del documento e imprimir el objeto RegReply. Observar * que una aplicación real actuaría según el status_code * del objeto RegReply. */ private void readRegistrationReply(InputStream stream) throws Exception { // Crear el constructor de documento DocumentBuilder db = dbf.newDocumentBuilder(); // // Establecer ErrorHandler antes del análisis // Utilizar el descriptor predeterminado. // db.setErrorHandler(new DefaultHandler()); // Analizar el archivo de entrada Document doc = db.parse(stream); RegReply reply = new RegReply(doc); reply.print(System.out); } /* variables de miembro privado */ private InetAddress regIp; private int regPort; private EventReceptionThread evtThr; private String regs[]; /* variables de miembro público */ public int localPort; public DocumentBuilderFactory dbf; } /* * clase EventReceptionThread * ---------------------------- * Consultar los anteriores comentarios de cabecera del archivo. */ class EventReceptionThread extends Thread { /* * constructor EventReceptionThread * ---------------------------------- * Crea un ServerSocket nuevo, vinculado al nombre de sistema local y * un puerto comodín. */ public EventReceptionThread(CrnpClient clientIn) throws IOException { /* * mantener una referencia al cliente para que se pueda rellamar * al recibir un evento. */ client = clientIn; /* * Especificar la IP al que se debería establecer el vínculo. * Es sencillamente la ip del sistema local. Si hay más de una * interfaz pública configurada en esta máquina, se irá a * cualquiera que indique InetAddress.getLocalHost. * */ listeningSock = new ServerSocket(client.localPort, 50, InetAddress.getLocalHost()); System.out.println(listeningSock); } /* * run * --- * Invocado por el método Thread.Start. * * Realiza un bucle infinito, a la espera de conexiones de entrada * de ServerSocket. * * A medida que se acepta cada conexión entrante, se crea un objeto de * de evento del canal de xml, que luego se pasa al objeto * CrnpClient para su proceso. */ public void run() { /* * Bucle infinito. */ try { // // Crea el generador de documento con el // generador de documentos de CrnpClient. // DocumentBuilder db = client.dbf.newDocumentBuilder(); // // Establecer un ErrorHandler antes de analizar // Utilizar el descriptor predeterminado. // db.setErrorHandler(new DefaultHandler()); while(true) { /* Esperar rellamada del servidor */ Socket sock = listeningSock.accept(); // Analizar el archivo de entrada Document doc = db.parse(sock.getInputStream()); Event event = new Event(doc); client.processEvent(event); /* Cerrar el zócalo */ sock.close(); } // INACCESIBLE } catch (Exception e) { System.out.println(e); System.exit(1); } } /* variables de miembro privado */ private ServerSocket listeningSock; private CrnpClient client; } /* * clase NVPair * ----------- * Esta clase guarda un par de nombre-valor (ambas cadenas). Sabe como crear * un mensaje XML NVPAIR desde sus miembros y cómo analizar un elemento * XML NVPAIR en sus miembros. * * Observar que la especificación de formato de un NVPAIR permite varios * valores. Se ha realizado la simplificación a un solo valor. */ class NVPair { /* * Dos constructores: el primero crea un NVPair vacío; el segundo * crea un NVPair a partir de un elemento XML NVPAIR. */ public NVPair() { name = value = null; } public NVPair(Element elem) { retrieveValues(elem); } /* * Configuradores públicos. */ public void setName(String nameIn) { name = nameIn; } public void setValue(String valueIn) { value = valueIn; } /* * Imprime el nombre y valor en una sola línea. */ public void print(PrintStream out) { out.println("NAME=" + name + " VALUE=" + value); } /* * createXmlElement * ------------------ * Construye un elemento XML NVPAIR a partir de las variables * de miembro. Toma el documento como parámetro para que pueda * crear el elemento. */ public Element createXmlElement(Document doc) { // Crear el elemento. Element nvpair = (Element) doc.createElement("NVPAIR"); // // Agregar el nombre. Observar que el nombre real es // una sección CDATA separada. // Element eName = doc.createElement("NAME"); Node nameData = doc.createCDATASection(name); eName.appendChild(nameData); nvpair.appendChild(eName); // // Agregar el valor. Observar que el valor real es // una sección CDATA separada. // Element eValue = doc.createElement("VALUE"); Node valueData = doc.createCDATASection(value); eValue.appendChild(valueData); nvpair.appendChild(eValue); return (nvpair); } /* * retrieveValues * ---------------- * Analizar el elemento XML para recuperar el nombre y valor. */ private void retrieveValues(Element elem) { Node n; NodeList nl; // // Buscar el elemento NAME // nl = elem.getElementsByTagName("NAME"); if (nl.getLength() != 1) { System.out.println("Error al analizar: no se puede encontrar " + "nodo NAME."); return; } // // Obtener la sección TEXT // n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { System.out.println("Error al analizar: no se puede encontrar " + "sección TEXT."); return; } // Recuperar el valor name = n.getNodeValue(); // // Ahora, obtener el elemento de valor // nl = elem.getElementsByTagName("VALUE"); if (nl.getLength() != 1) { System.out.println("Error al analizar: no se puede encontrar " + "nodo VALUE."); return; } // // Obtener la sección TEXT // n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { System.out.println("Error al analizar: no se puede encontrar " + "sección TEXT."); return; } // Recuperar el valor value = n.getNodeValue(); } /* * Accesores públicos */ public String getName() { return (name); } public String getValue() { return (value); } // Variables de miembro privado private String name, value; } /* * class Event * ----------- * Esta clase guarda un evento que consta de una clase, subclase, fabricante, * editor y lista de pares de nombre-valor. Sabe cómo construir un elemento * XML SC_EVENT_REG a partir de sus miembros y cómo analizar un elemento * XML SC_EVENT en sus miembros. Observar que existe una asimetría aquí: se * analizan los elementos SC_EVENT, pero se construyen elementos SC_EVENT_REG. * Esto se debe a que los elementos SC_EVENT_REG se utilizan en los mensajes * de registro (que hay que construir), en tanto que los elementos SC_EVENT se * utilizan en la entrega de eventos (que hay que analizar). La única diferencia * es que los elementos SC_EVENT_REG no tienen fabricante ni editor. */ class Event { /* * Dos constructores: el primero crea un evento vacío; el segundo * crea un evento a partir de un documento XML SC_EVENT. */ public Event() { regClass = regSubclass = null; nvpairs = new Vector(); } public Event(Document doc) { nvpairs = new Vector(); // // Convertir el documento en una cadena para imprimir con fines // de depuración. // DOMSource domSource = new DOMSource(doc); 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; } System.out.println(strWrite.toString()); // Realizar el análisis real. retrieveValues(doc); } /* * Configuradores públicos */ public void setClass(String classIn) { regClass = classIn; } public void setSubclass(String subclassIn) { regSubclass = subclassIn; } public void addNvpair(NVPair nvpair) { nvpairs.add(nvpair); } /* * createXmlElement * ------------------ * Construye un elemento XMl SC_EVENT_REG a partir de las variables de * los miembros. Toma el documento como parámetro para que pueda crear * el elemento. Depende de la capacidad del NVPair createXmlElement. */ 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); } /* * Imprime las variables de miembros en varias líneas. */ 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); } } /* * retrieveValues * ---------------- * Analizar el documento XML para recuperar la clase, subclase, * fabricante, editor y pares nombre/valor. */ private void retrieveValues(Document doc) { Node n; NodeList nl; // // Buscar el elemento SC_EVENT. // nl = doc.getElementsByTagName("SC_EVENT"); if (nl.getLength() != 1) { System.out.println("Error al analizar: no se puede encontrar " + "nodo SC_EVENT."); return; } n = nl.item(0); // // Recuperar los valores de los atributos CLASS, SUBCLASS, // VENDOR y PUBLISHER. // regClass = ((Element)n).getAttribute("CLASS"); regSubclass = ((Element)n).getAttribute("SUBCLASS"); publisher = ((Element)n).getAttribute("PUBLISHER"); vendor = ((Element)n).getAttribute("VENDOR"); // // Recuperar todos los pares n-v // for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { nvpairs.add(new NVPair((Element)child)); } } /* * Métodos de accesores públicos. */ 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); } // Variables de miembro privado. private String regClass, regSubclass; private Vector nvpairs; private String vendor, publisher; } /* * class CallbackReg * ----------- * Esta clase guarda un puerto y regType (ambas cadenas) y una lista de * eventos. Sabe cómo construir un mensaje XML SC_CALLBACK_REG de sus * miembros. * * Observar que esta clase no necesita poder analizar mensajes * SC_CALLBACK_REG porque sólo el servidor CRNP debe analizar mensajes * SC_CALLBACK_REG */ class CallbackReg { // Definiciones útiles para el método setRegType 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(); } /* * Configuradores públicos. */ 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, regType no válido " + regTypeIn); regType = "ADD_CLIENT"; break; } } public void addRegEvent(Event regEvent) { regEvents.add(regEvent); } /* * convertToXml * ------------------ * Construye un documento XML SC_CALLBACK_REG a partir de variables * de miembro. Depende de la capacidad del evento createXmlElement. */ public String convertToXml() { Document document = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); } catch (ParserConfigurationException pce) { // No se puede crear el analizador con las opciones especificadas pce.printStackTrace(); System.exit(1); } Element root = (Element) document.createElement( "SC_CALLBACK_REG"); root.setAttribute("VERSION", "1.0"); root.setAttribute("PORT", port); root.setAttribute("REG_TYPE", regType); for (int i = 0; i < regEvents.size(); i++) { Event tempEvent = (Event) (regEvents.elementAt(i)); root.appendChild(tempEvent.createXmlElement( document)); } document.appendChild(root); // // Ahora, convertir el documento en cadena. // 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()); } // variables de miembro privado private String port; private String regType; private Vector regEvents; } /* * class RegReply * ----------- * Esta clase guarda status_code y status_msg (ambas cadenas). * Sabe cómo analizar un elemento XML SC_REPLY en sus miembros. */ class RegReply { /* * El constructor único toma un documento XML y lo analiza. */ public RegReply(Document doc) { // // Convertir el documento en cadena. // DOMSource domSource = new DOMSource(doc); 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; } System.out.println(strWrite.toString()); retrieveValues(doc); } /* * Accesores públicos */ public String getStatusCode() { return (statusCode); } public String getStatusMsg() { return (statusMsg); } /* * Imprime la información en una sola línea. */ public void print(PrintStream out) { out.println(statusCode + ": " + (statusMsg != null ? statusMsg : "")); } /* * retrieveValues * ---------------- * Analiza el documento XML para recuperar statusCode y statusMsg. */ private void retrieveValues(Document doc) { Node n; NodeList nl; // // Buscar el elemento SC_REPLY. // nl = doc.getElementsByTagName("SC_REPLY"); if (nl.getLength() != 1) { System.out.println("Error al analizar: no se puede encontrar " + "nodo SC_REPLY."); return; } n = nl.item(0); // Recuperar el valor del atributo STATUS_CODE statusCode = ((Element)n).getAttribute("STATUS_CODE"); // // Buscar el elemento SC_STATUS_MSG // nl = ((Element)n).getElementsByTagName("SC_STATUS_MSG"); if (nl.getLength() != 1) { System.out.println("Error al analizar: no se puede encontrar " + "nodo SC_STATUS_MSG."); return; } // // Obtener la sección TEXT, si la hubiera. // n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { // Sin error, si no lo hay // se retorna discretamente. return; } // Recuperar el valor statusMsg = n.getNodeValue(); } // variables de miembro privado private String statusCode; private String statusMsg; }