Sun Cluster 資料服務開發者指南 (適用於 Solaris 作業系統)

建立使用 CRNP 的 Java 應用程式

以下範例說明如何開發使用 CRNP 的名為 CrnpClient 的簡單 Java 應用程式。 該應用程式在叢集上的 CRNP 伺服器中註冊事件回呼、偵聽事件回呼、透過列印事件內容來處理這些事件。 在終止之前,該應用程式會取消註冊其對事件回呼的要求。

檢查此範例時,請注意以下幾點。

設定環境

首先,您需要設定環境。

  1. 下載並安裝 JAXP、正確版本的 Java 編譯器、虛擬機器。

    您可以在 http://java.sun.com/xml/jaxp/index.html 上找到說明。


    註解 –

    此範例需要 Java 1.3.1 或更高版本的 Java。


  2. 確定您在編譯指令行中指定 classpath,以便編譯器可以找到 JAXP 類別。 從來源檔所在的目錄,輸入:


    % javac -classpath JAXP_ROOT/dom.jar:JAXP_ROOTjaxp-api. \
    jar:JAXP_ROOTsax.jar:JAXP_ROOTxalan.jar:JAXP_ROOT/xercesImpl \
    .jar:JAXP_ROOT/xsltc.jar -sourcepath . SOURCE_FILENAME.java
    

    其中,JAXP_ROOT 是 JAXP 的 jar 檔案所在目錄的絕對路徑或相對路徑,SOURCE_FILENAME 是 Java 來源檔的名稱。

  3. 執行應用程式時,請指定 classpath,以便該應用程式可以載入適當的 JAXP 類別檔案 (請注意,classpath 中的第一條路徑為目前目錄):


    java -cp .:JAXP_ROOT/dom.jar:JAXP_ROOTjaxp-api. \
    jar:JAXP_ROOTsax.jar:JAXP_ROOTxalan.jar:JAXP_ROOT/xercesImpl \
    .jar:JAXP_ROOT/xsltc.jar SOURCE_FILENAME ARGUMENTS
    

    既然已配置了環境,您可以開發應用程式。

開始

在範例的此部分,您將使用剖析指令行引數與建構 CrnpClient 物件的主要方法,來建立名為 CrnpClient 的基本類別。 此物件將指令行引數傳送至類別、等待使用者終止該應用程式、對 CrnpClient 呼叫 shutdown,然後結束。

CrnpClient 類別的建構元需要執行下列作業:

    建立實施前導邏輯的 Java 程式碼。

    以下範例顯示 CrnpClient 類別的架構程式碼。 建構元中所參考的四種輔助說明方法與關閉方法的實作將在以後加以說明。 請注意,匯入您需要的所有套件之程式碼顯示如下。


    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;
    }

    將在以後對成員變數進行更詳細的論述。

剖析指令行引數

    若要查看剖析指令行引數的方式,請參閱附錄 G, CrnpClient.java 應用程式 中的程式碼。

定義事件接收執行緒

在程式碼中,您需要確定事件接收是在獨立的執行緒中執行的,以便應用程式可以在事件執行緒封鎖並等待事件回呼時繼續進行其他工作。


註解 –

將在以後對設定 XML 加以論述。


  1. 在程式碼中,定義稱為 EventReceptionThreadThread 子類別 (用來建立 ServerSocket 並等待事件到達套接字)。

    在範例程式碼的此部分,事件既未被讀取,也未經過處理。 將在以後對讀取與處理事件加以論述。 EventReceptionThread 在萬用字元網際網路協定位址建立 ServerSocketEventReceptionThread 也保持對 CrnpClient 物件的參考,以便 EventReceptionThread 可以將事件發送給 CrnpClient 物件以進行處理。


    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();
                                    // Construct event from the sock stream and process it
                                    sock.close();
                            }
                            // UNREACHABLE
    
                    } catch (Exception e) {
                            System.out.println(e);
                            System.exit(1);
                    }
            }
    
            /* private member variables */
            private ServerSocket listeningSock;
            private CrnpClient client;
    }

  2. 既然您看到了 EventReceptionThread 類別的工作方式,因此請建構 createEvtRecepThr 物件:


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

註冊與取消註冊回呼

註冊作業包括:

  1. 建立實施前導邏輯的 Java 程式碼。

    以下範例顯示 CrnpClient 類別 (由 CrnpClient 建構元呼叫) 的 registerCallbacks 方法的實作。 將在以後對 createRegistrationString()readRegistrationReply() 的呼叫加以更詳細的說明。

    regIpregPort 均為由建構元設定的物件成員。


    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. 實施 unregister 方法。 此方法由 CrnpClientshutdown 方法呼叫。 將在以後對 createUnregistrationString 的實作加以更詳細的說明。


    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();
    }

產生 XML

既然您已設定應用程式的結構,並已寫入所有網路程式碼,因此您可以寫入產生與剖析 XML 的程式碼。 透過寫入產生 SC_CALLBACK_REG XML 註冊訊息的程式碼來啟動。

SC_CALLBACK_REG 訊息由註冊類型 (ADD_CLIENTREMOVE_CLIENTADD_EVENTS 或者 REMOVE_EVENTS)、回呼通訊埠、使人感興趣的事件之清單所組成。 每個事件由類別與子類別組成,其後跟隨名稱與值對的清單。

在範例的此部分,您將寫入儲存註冊類型、回呼通訊埠及註冊事件清單的 CallbackReg 類別。 此類別還可以將自身串列化為 SC_CALLBACK_REG XML 訊息。

此類別使人感興趣的方法為 convertToXml 方法,該方法建立類別成員的 SC_CALLBACK_REG XML 訊息字串。 在 http://java.sun.com/xml/jaxp/index.html 上的 JAXP 說明文件中對此方法中的程式碼進行了更加詳細的說明。

Event 類別的實作顯示如下。 請注意,CallbackReg 類別使用儲存一個事件並可以將該事件轉換為 XML ElementEvent 類別。

  1. 建立實施前導邏輯的 Java 程式碼。


    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) {
                            // Parser with specified options can't be built
                            pce.printStackTrace();
                            System.exit(1);
                    }
    
                    // Create the root element
                    Element root = (Element) document.createElement(
                        "SC_CALLBACK_REG");
    
                    // Add the attributes
                    root.setAttribute("VERSION", "1.0");
                    root.setAttribute("PORT", port);
                    root.setAttribute("regType", regType);
    
                    // Add the events
                    for (int i = 0; i < regEvents.size(); i++) {
                            Event tempEvent = (Event)
                                (regEvents.elementAt(i));
                            root.appendChild(tempEvent.createXmlElement(
                                document));
                    }
                    document.appendChild(root);
    
                    // Convert the whole thing to a string
                    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. 實施 Event 類別與 NVPair 類別。

    請注意,CallbackReg 類別使用 Event 類別 (其自身使用 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;
    }

建立註冊訊息與取消註冊訊息

既然您已建立了產生 XML 訊息的輔助說明類別,因此可以寫入 createRegistrationString 方法的實作。 此方法由註冊與取消註冊回呼中說明的 registerCallbacks 方法呼叫。

createRegistrationString 建構 CallbackReg 物件,並設定其註冊類型與通訊埠。 然後,createRegistrationString 使用 createAllEventcreateMembershipEventcreateRgEventcreateREvent 輔助說明方法建構各種事件。 在建立 CallbackReg 物件之後,便會將每個事件加入該物件。 最後,createRegistrationStringCallbackReg 物件呼叫 convertToXml 方法,以擷取 String 形式的 XML 訊息。

請注意,regs 成員變數儲存使用者提供給應用程式的指令行引數。 第五個引數與後續引數指定應用程式應該註冊的事件。 第四個引數指定註冊的類型,但在此範例中忽略。 附錄 G, CrnpClient.java 應用程式 中的完整程式碼顯示如何使用此第四個引數。

  1. 建立實施前導邏輯的 Java 程式碼。


    private String createRegistrationString() throws Exception
    {
            CallbackReg cbReg = new CallbackReg();
            cbReg.setPort("" + localPort);
    
            cbReg.setRegType(CallbackReg.ADD_CLIENT);
    
            // add the events
            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. 建立取消註冊字串。

    建立取消註冊字串要比建立註冊字串容易,因為您無需考慮事件:


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

設定 XML 剖析器

您現在已經為應用程式建立了網路程式碼與 XML 產生程式碼。 最後一個步驟是剖析與處理註冊回覆與事件回呼。 CrnpClient 建構元呼叫 setupXmlProcessing 方法。 此方法建立 DocumentBuilderFactory 物件,並設定有關該物件的各種剖析屬性。 在 http://java.sun.com/xml/jaxp/index.html 上的 JAXP 說明文件中對此方法進行了更加詳細的說明。

    建立實施前導邏輯的 Java 程式碼。


    private void setupXmlProcessing() throws Exception
    {
            dbf = DocumentBuilderFactory.newInstance();
    
            // We don't need to bother validating
            dbf.setValidating(false);
            dbf.setExpandEntityReferences(false);
    
            // We want to ignore comments and whitespace
            dbf.setIgnoringComments(true);
            dbf.setIgnoringElementContentWhitespace(true);
    
            // Coalesce CDATA sections into TEXT nodes.
            dbf.setCoalescing(true);
    }

剖析註冊回覆

若要剖析 CRNP 伺服器為回應註冊訊息與取消註冊訊息所發送的 SC_REPLY XML 訊息,您需要 RegReply 輔助說明類別。 您可以從 XML 文件建構此類別。 此類別為狀況碼與狀況訊息提供 accessor。 若要剖析來自伺服器的 XML 串流,您需要建立新的 XML 文件,並使用該文件的剖析方法 (http://java.sun.com/xml/jaxp/index.html 上的 JAXP 說明文件中對此方法進行了更加詳細的說明)。

  1. 建立實施前導邏輯的 Java 程式碼。

    請注意,readRegistrationReply 方法使用新的 RegReply 類別。


    private void readRegistrationReply(InputStream stream) throws Exception
    {
            // Create the document builder
            DocumentBuilder db = dbf.newDocumentBuilder();
            db.setErrorHandler(new DefaultHandler());
    
            //parse the input file
            Document doc = db.parse(stream);
    
            RegReply reply = new RegReply(doc);
            reply.print(System.out);
    }

  2. 實施 RegReply 類別。

    請注意,retrieveValues 方法在 XML 文件的 DOM 樹狀結構中行走,然後取出狀況碼與狀況訊息。 http://java.sun.com/xml/jaxp/index.html 上的 JAXP 說明文件包含更多詳細資訊。


    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;
    
                    // Find the SC_REPLY element.
                    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);
    
                    // Retrieve the value of the statusCode attribute
                    statusCode = ((Element)n).getAttribute("STATUS_CODE");
    
                    // Find the SC_STATUS_MSG element
                    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;
                    }
                    // Get the TEXT section, if there is one.
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                    // Not an error if there isn't one, so we just silently return.
                            return;
                    }
    
                    // Retrieve the value
                    statusMsg = n.getNodeValue();
            }
    
            private String statusCode;
            private String statusMsg;
    }

剖析回呼事件

最後一個步驟是剖析與處理實際的回呼事件。 若要輔助此作業,您要修改在產生 XML中建立的 Event 類別,以便此類別可以從 XML 文件建構 Event,以及建立 XML Element。 此變更需要其他建構元 (採用 XML 文件)、retrieveValues 方法、加入兩個成員變數 (vendorpublisher)、適用於所有欄位的 accessor 方法,最後還需要列印方法。

  1. 建立實施前導邏輯的 Java 程式碼。

    請注意,此程式碼類似於剖析註冊回覆中說明的 RegReply 類別之程式碼。


            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;
    
                    // Find the SC_EVENT element.
                    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);
    
                    //
                    // Retrieve the values of the CLASS, SUBCLASS,
                    // VENDOR and PUBLISHER attributes.
                    //
                    regClass = ((Element)n).getAttribute("CLASS");
                    regSubclass = ((Element)n).getAttribute("SUBCLASS");
                    publisher = ((Element)n).getAttribute("PUBLISHER");
                    vendor = ((Element)n).getAttribute("VENDOR");
    
                    // Retrieve all the nv pairs
                    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. 針對支援 XML 剖析的 NVPair 類別實施其他建構元與方法。

    變更步驟 1 中所顯示的 Event 類別需要對 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;
    
                    // Find the NAME element
                    nl = elem.getElementsByTagName("NAME");
                    if (nl.getLength() != 1) {
                       System.out.println("Error in parsing: can't find "
                           + "NAME node.");
                       return;
                    }
                    // Get the TEXT section
                    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;
                    }
    
                    // Retrieve the value
                    name = n.getNodeValue();
    
                    // Now get the value element
                    nl = elem.getElementsByTagName("VALUE");
                    if (nl.getLength() != 1) {
                       System.out.println("Error in parsing: can't find "
                           + "VALUE node.");
                       return;
                    }
                    // Get the TEXT section
                    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;
                    }
    
                    // Retrieve the value
                    value = n.getNodeValue();
                    }
    
            public String getName()
            {
                    return (name);
            }
    
            public String getValue()
            {
                    return (value);
            }
    }

  3. 在等待事件回呼的 EventReceptionThread 中實施 while 迴圈 (在定義事件接收執行緒中有對 EventReceptionThread 的說明)。


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

執行應用程式

    執行應用程式。


    # java CrnpClient crnpHost crnpPort localPort ...
    

    附錄 G, CrnpClient.java 應用程式 中列出了 CrnpClient 應用程式的完整程式碼。