この付録では、CrnpClient.java アプリケーションの完全なコードを示します (詳細は、第 12 章「CRNP」を参照)。
/* * CrnpClient.java * ================ * * XML 解析についての注意: * * このプログラムは、Sun Java Architecture for XML Processing (JAXP) API を * 使用しています。API ドキュメントや利用についての情報は、 * http://java.sun.com/xml/jaxp/index.html を参照してください。 * * このプログラムは、Java 1.3.1 以降を対象に作成されています。 * * プログラムの概要: * * このプログラムのメインスレッドは、CrnpClient オブジェクトを作成し、 * ユーザーがデモを終了するのを待機し、CrnpClient オブジェクトで * shutdown を呼び出し、最後にプログラムを終了します。 * * CrnpClient コンストラクタは、EventReceptionThread オブジェクトを作成し、 * (コマンド行で指定されたホストとポートを使用して) CRNP サーバーに対して * 接続を開き、(コマンド行の指定にもとづいて) 登録メッセージを作成し、登録 * メッセージを送信し、応答の読み取りと解析を行います。 * * EventReceptionThread は、このプログラムが動作するマシンのホスト名と * コマンド行に指定されるポートにバインドされる待機ソケットを作成します。 * EventReceptionThread は、イベントコールバックの着信を待機し、受信した * ソケットストリームから XML ドキュメントを構築し、これを CrnpClient * オブジェクトに返して処理を行わせます。 * * CrnpClient 内の shutdown メソッドは、単に登録解除用の * (REMOVE_CLIENT) SC_CALLBACK_REG メッセージを crnp サーバーへ送信するだけ * です。 * * エラー処理についての注意: 説明を簡潔にするため、このプログラムはほとんどの * エラーに対して単に終了するだけですが、実際のアプリケーションではさまざまな * 方法でエラー処理がなされます (適宜再試行するなど)。 */ // 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.*; // 標準パッケージ import java.net.*; import java.io.*; import java.util.*; /* * class CrnpClient * ----------------- * 上記のファイルヘッダーコメントを参照。 */ class CrnpClient { /* * main * ---- * 実行のエントリポイント main は、コマンド行引数の数を検証し、 * すべての作業を行う CrnpClient インスタンスを作成する。 */ public static void main(String []args) { InetAddress regIp = null; int regPort = 0, localPort = 0; /* コマンド行引数の数を検証する */ if (args.length < 4) { System.out.println( "Usage: java CrnpClient crnpHost crnpPort " + "localPort (-ac | -ae | -re) " + "[(M | A | RG=name | R=name) [...]]"); System.exit(1); } /* * コマンド行には crnp サーバーの IP とポート、待機する * ローカルポート、登録タイプを示す引数が入る。 */ 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 を作成する。 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 コンストラクタ * ------------------------- * crnp サーバーとの通信方法を知るためにコマンド行引数を解析し、 * イベント受信スレッドを作成し、このスレッドの実行を開始し、XML * DocumentBuilderFactory オブジェクトを作成し、最後に crnp * サーバーにコールバックの登録を行う。 */ public CrnpClient(InetAddress regIpIn, int regPortIn, int localPortIn, String []clArgs) { try { regIp = regIpIn; regPort = regPortIn; localPort = localPortIn; regs = clArgs; /* * xml 処理用のドキュメントビルダー * ファクトリを設定する。 */ setupXmlProcessing(); /* * ServerSocket を作成してこれをローカル IP と * ポートにバインドする EventReceptionThread を * 作成する。 */ createEvtRecepThr(); /* * crnp サーバーに登録する。 */ registerCallbacks(); } catch (Exception e) { System.out.println(e.toString()); System.exit(1); } } /* * processEvent * --------------- * CrnpClient (イベントコールバックを受信する際に * EventReceptionThread によって使用される) にコールバックする。 */ public void processEvent(Event event) { /* * ここでは、説明の都合上、単純にイベントを System.out * に出力。実際のアプリケーションでは、通常、イベント * をなんらかの方法で使用する。 */ event.print(System.out); } /* * shutdown * ------------- * CRNP サーバーに対する登録を解除する。 */ public void shutdown() { try { /* サーバーに登録解除メッセージを送信する */ unregister(); } catch (Exception e) { System.out.println(e); System.exit(1); } } /* * ====================== * private ヘルパーメソッド * ====================== */ /* * setupXmlProcessing * -------------------- * xml 応答と xml イベントを解析するためにドキュメント * ビルダーファクトリを作成する。 */ private void setupXmlProcessing() throws Exception { dbf = DocumentBuilderFactory.newInstance(); // わざわざ検証する必要はない。 dbf.setValidating(false); dbf.setExpandEntityReferences(false); // コメントと空白文字は無視したい。 dbf.setIgnoringComments(true); dbf.setIgnoringElementContentWhitespace(true); // CDATA セクションを TEXT ノードに結合する。 dbf.setCoalescing(true); } /* * createEvtRecepThr * ------------------- * 新しい EventReceptionThread オブジェクトを作成し、待機 * ソケットがバインドされる IP とポートを保存し、スレッドの * 実行を開始する。 */ private void createEvtRecepThr() throws Exception { /* スレッドオブジェクトを作成する */ evtThr = new EventReceptionThread(this); /* * イベント配信コールバックを待機し始めるために * スレッドの実行を開始する。 */ evtThr.start(); } /* * registerCallbacks * ------------------ * crnp サーバーに対するソケット接続を作成し、 * イベント登録メッセージを送信する。 */ private void registerCallbacks() throws Exception { System.out.println("About to register"); /* * crnp サーバーの登録 IP / ポートに接続されたソケット * を作成し、登録情報を送信する。 */ Socket sock = new Socket(regIp, regPort); String xmlStr = createRegistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); /* * 応答を読み取る。 */ readRegistrationReply(sock.getInputStream()); /* * ソケット接続を閉じる。 */ sock.close(); } /* * unregister * ---------- * registerCallbacks の場合と同様に、crnp サーバーに対する * ソケット接続を作成し、登録解除メッセージを送信し、 * サーバーからの応答を待機し、ソケットを閉じる。 */ private void unregister() throws Exception { System.out.println("About to unregister"); /* * crnp サーバーの登録 IP / ポートに接続された * ソケットを作成し、登録解除情報を送信する。 */ Socket sock = new Socket(regIp, regPort); String xmlStr = createUnregistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); /* * 応答を読み取る。 */ readRegistrationReply(sock.getInputStream()); /* * ソケット接続を閉じる。 */ sock.close(); } /* * createRegistrationString * ------------------ * このプログラムのコマンド行引数にもとづいて CallbackReg * オブジェクトを作成し、CallbackReg オブジェクトから XML * 文字列を取得する。 */ private String createRegistrationString() throws Exception { /* * 実際の CallbackReg クラスを作成し、ポートを設定する。 */ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); // 登録タイプを設定する。 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("Invalid reg type: " + regs[3]); System.exit(1); } // イベントを追加する。 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 * ---------------- * クラス EC_Cluster を使用して (サブクラスは使用しない) * XML 登録イベントを作成する。 */ private Event createAllEvent() {EC_Cluster Event allEvent = new Event(); allEvent.setClass("EC_Cluster"); return (allEvent); } /* * createMembershipEvent * ---------------------- * クラス EC_Cluster、サブクラス ESC_cluster_memberhip を * 使用して XML 登録イベントを作成する。 */ private Event createMembershipEvent() { Event membershipEvent = new Event(); membershipEvent.setClass("EC_Cluster"); membershipEvent.setSubclass("ESC_cluster_membership"); return (membershipEvent); } /* * createRgEvent * ---------------- * クラス EC_Cluster、サブクラス ESC_cluster_rg_state、 * および "rg_name" nvpair (入力パラメータにもとづく) を * 1 つ使用して XML 登録イベントを作成する。 */ private Event createRgEvent(String rgname) { /* * rgname リソースグループ * 用のリソースグループ状態変更イベントを作成する。 * このイベントタイプには、どのリソースグループに興味 * があるのかを示すため、名前 / 値ペア (nvpair) を指定 * する。 */ /* * イベントオブジェクトを作成し、クラスとサブクラスを設定する。 */ Event rgStateEvent = new Event(); rgStateEvent.setClass("EC_Cluster"); rgStateEvent.setSubclass("ESC_cluster_rg_state"); /* * nvpair オブジェクトを作成し、これをこのイベントに追加する。 */ NVPair rgNvpair = new NVPair(); rgNvpair.setName("rg_name"); rgNvpair.setValue(rgname); rgStateEvent.addNvpair(rgNvpair); return (rgStateEvent); } /* * createREvent * ---------------- * クラス EC_Cluster、サブクラス ESC_cluster_r_state、 * および "r_name" nvpair (入力パラメータにもとづく) を * 1 つ使用して XML 登録イベントを作成する。 */ private Event createREvent(String rname) { /* * rgname リソース用の * リソース状態変更イベントを作成する。 * このイベントタイプには、どのリソースグループに興味 * があるかを示すため、名前 / 値ペア (nvpair) を指定 * する。 */ 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 * ------------------ * REMOVE_CLIENT CallbackReg オブジェクトを作成し、 * CallbackReg オブジェクトから XML 文字列を取得する。 */ private String createUnregistrationString() throws Exception { /* * CallbackReg オブジェクトを作成する。 */ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); cbReg.setRegType(CallbackReg.REMOVE_CLIENT); /* * 登録を OutputStream に整列化する。 */ String xmlStr = cbReg.convertToXml(); // デバッグのために文字列を出力する。 System.out.println(xmlStr); return (xmlStr); } /* * readRegistrationReply * ------------------------ * xml を解析してドキュメントにし、このドキュメントから * RegReply オブジェクトを構築し、RegReply オブジェクトを * 出力する。実際のアプリケーションでは、通常、RegReply * オブジェクトの status_code にもとづいて処理をする。 */ private void readRegistrationReply(InputStream stream) throws Exception { // ドキュメントビルダーを作成する。 DocumentBuilder db = dbf.newDocumentBuilder(); // // 解析前に ErrorHandler を設定する。 // ここではデフォルトハンドラを使用。 // db.setErrorHandler(new DefaultHandler()); // 入力ファイルを解析する。 Document doc = db.parse(stream); RegReply reply = new RegReply(doc); reply.print(System.out); } /* private 指定のメンバー変数 */ private InetAddress regIp; private int regPort; private EventReceptionThread evtThr; private String regs[]; /* public 指定のメンバー変数 */ public int localPort; public DocumentBuilderFactory dbf; } /* * クラス EventReceptionThread * ---------------------------- * 上記のファイルヘッダーコメントを参照。 */ class EventReceptionThread extends Thread { /* * EventReceptionThread コンストラクタ * ---------------------------------- * ローカルホスト名とワイルドカードポートにバインドされる * 新しい ServerSocket を作成する。 */ public EventReceptionThread(CrnpClient clientIn) throws IOException { /* * イベントの取得時に再度呼び返すことができるように、 * クライアントに対する参照を保持する。 */ client = clientIn; /* * バインドする IP を指定する。これは、ローカル * ホスト IP である。このマシンに複数のパブリック * インタフェースが構成されている場合は、 * InetAddress.getLocalHost によって検出される * ものをどれでも使用する。 * */ listeningSock = new ServerSocket(client.localPort, 50, InetAddress.getLocalHost()); System.out.println(listeningSock); } /* * run * --- * Thread.Start メソッドによって呼び出される。 * * ServerSocket で着信接続を待機し、永続的にループする。 * * 各着信接続が受け入れられる際に xml ストリームから * Event オブジェクトが作成される。続いてこのオブジェクト * が CrnpClient オブジェクトに返されて処理される。 */ public void run() { /* * 永続的にループする。 */ try { // // CrnpClient 内のドキュメントビルダーファクトリを // 使用してドキュメントビルダーを作成する。 // DocumentBuilder db = client.dbf.newDocumentBuilder(); // // 解析前に ErrorHandler を設定する。 // ここではデフォルトハンドラを使用。 // db.setErrorHandler(new DefaultHandler()); while(true) { /* サーバーからのコールバックを待機 */ Socket sock = listeningSock.accept(); // 入力ファイルを解析する。 Document doc = db.parse(sock.getInputStream()); Event event = new Event(doc); client.processEvent(event); /* ソケットを閉じる */ sock.close(); } // 到達不能 } catch (Exception e) { System.out.println(e); System.exit(1); } } /* private 指定のメンバー変数 */ private ServerSocket listeningSock; private CrnpClient client; } /* * クラス NVPair * ----------- * このクラスは名前 / 値ペア (両方とも文字列) を格納する。 * このクラスは、そのメンバーから NVPAIR XML メッセージを構築し、 * NVPAIR XML 要素を解析してそのメンバーにすることができる。 * * NVPAIR の形式仕様では複数の値が許可されているが、ここでは * 単純に値は 1 つだけという前提を下す。 */ class NVPair { /* * 2 つのコンストラクタ: 最初のコンストラクタは空の NVPair を * 作成する。2 つ目は NVPAIR XML 要素から NVPair を作成する。 */ public NVPair() { name = value = null; } public NVPair(Element elem) { retrieveValues(elem); } /* * public 指定のセッター。 */ public void setName(String nameIn) { name = nameIn; } public void setValue(String valueIn) { value = valueIn; } /* * 1 行で名前と値を出力する。 */ public void print(PrintStream out) { out.println("NAME=" + name + " VALUE=" + value); } /* * createXmlElement * ------------------ * メンバー変数から NVPAIR XML 要素を作成する。 * この要素を作成できるように、ドキュメントをパラメータとして * 受け付ける。 */ public Element createXmlElement(Document doc) { // 要素を作成する。 Element nvpair = (Element) doc.createElement("NVPAIR"); // // 名前を追加する。実際の名前は別の // CDATA セクションであることに注意。 // Element eName = doc.createElement("NAME"); Node nameData = doc.createCDATASection(name); eName.appendChild(nameData); nvpair.appendChild(eName); // // 値を追加する。実際の値は別の // CDATA セクションであることに注意。 // Element eValue = doc.createElement("VALUE"); Node valueData = doc.createCDATASection(value); eValue.appendChild(valueData); nvpair.appendChild(eValue); return (nvpair); } /* * retrieveValues * ---------------- * XML 要素を解析して名前と値を取得する。 */ private void retrieveValues(Element elem) { Node n; NodeList nl; // // NAME 要素を検出する。 // nl = elem.getElementsByTagName("NAME"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "NAME node."); return; } // // 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; } // 値を取得する。 name = n.getNodeValue(); // // ここで値要素を取得する。 // nl = elem.getElementsByTagName("VALUE"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "VALUE node."); return; } // // 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; } // 値を取得する。 value = n.getNodeValue(); } /* * public 指定のアクセッサ */ public String getName() { return (name); } public String getValue() { return (value); } // private 指定のメンバー変数 private String name, value; } /* * クラス Event * ----------- * このクラスは、クラス、サブクラス、ベンダー、パブリッシャー、名前 / * 値ペアのリストから成るイベントを格納する。このクラスでは、そのメンバー * から SC_EVENT_REG XML 要素を作成し、この要素を解析してそのメンバーに * することができる。次の非対称性に注意: SC_EVENT 要素を解析するが、 * 作成するのは SC_EVENT_REG 要素である。これは、SC_EVENT_REG 要素が * 登録メッセージ (これは作成の必要がある) 内で使用され、SC_EVENT 要素 * がイベント配信 (これは解析の必要がある) 内で使用されるためである。 * 違いは、SC_EVENT_REG 要素にはベンダーとパブリッシャーがないことだけ * である。 */ class Event { /* * 2 つのコンストラクタ: 最初のコンストラクタは空のイベントを * 作成し、2 つ目は SC_EVENT XML ドキュメントからイベントを * 作成する。 */ public Event() { regClass = regSubclass = null; nvpairs = new Vector(); } public Event(Document doc) { nvpairs = new Vector(); // // デバッグで使用できるようにドキュメントを文字列に // 変換して出力する。 // 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); } /* *public 指定のセッター。 */ public void setClass(String classIn) { regClass = classIn; } public void setSubclass(String subclassIn) { regSubclass = subclassIn; } public void addNvpair(NVPair nvpair) { nvpairs.add(nvpair); } /* * createXmlElement * ------------------ * メンバー変数から SC_EVENT_REG XML 要素を作成する。 * この要素を作成できるように、ドキュメントをパラメータとして * 受け付ける。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); } /* * メンバー変数を複数行に出力する。 */ 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 * ---------------- * XML ドキュメントを解析し、クラス、サブクラス、ベンダー、 * パブリッシャー、および nvpair を取得する。 */ private void retrieveValues(Document doc) { Node n; NodeList nl; // // 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); // // CLASS、SUBCLASS、VENDOR、および PUBLISHER // 属性の値を取得する。 // regClass = ((Element)n).getAttribute("CLASS"); regSubclass = ((Element)n).getAttribute("SUBCLASS"); publisher = ((Element)n).getAttribute("PUBLISHER"); vendor = ((Element)n).getAttribute("VENDOR"); // // すべての nv ペアを取得する。 // for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { nvpairs.add(new NVPair((Element)child)); } } /* * public 指定のアクセッサメソッド。 */ 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 指定のメンバー変数 private String regClass, regSubclass; private Vector nvpairs; private String vendor, publisher; } /* * クラス CallbackReg * ----------- * このクラスは、ポートと登録タイプ (どちらも文字列)、およびイベントリストを * 格納する。このクラスは、そのメンバーから SC_CALLBACK_REG XML メッセージ * を作成できる。 * * SC_CALLBACK_REG メッセージを解析する必要があるのは CRNP サーバー * だけであるため、このクラスで SC_CALLBACK_REG メッセージを解析でき * なくてもよい。 */ class CallbackReg { // 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(); } /* * public 指定のセッター。 */ 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); } /* * convertToXml * ------------------ * メンバー変数から SC_CALLBACK_REG XML ドキュメントを構築する。 * Event createXmlElement 機能を使用。 */ public String convertToXml() { Document document = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); } catch (ParserConfigurationException pce) { // 指定したオプションを持つパーサーを構築できない。 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); // // ここでドキュメントを文字列に変換する。 // 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 指定のメンバー変数 private String port; private String regType; private Vector regEvents; } /* * クラス RegReply * ----------- * このクラスは、status_code と status_msg (どちらも文字列) を格納する。 * このクラスは、SC_REPLY XML 要素を解析し、そのメンバーにできる。 */ class RegReply { /* * 1 つのコンストラクタが XML ドキュメントを受け入れて解析を行う。 */ public RegReply(Document doc) { // // ここでドキュメントを文字列に変換する。 // 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); } /* * public 指定のアクセッサ */ public String getStatusCode() { return (statusCode); } public String getStatusMsg() { return (statusMsg); } /* * 1 行で情報を出力する。 */ public void print(PrintStream out) { out.println(statusCode + ": " + (statusMsg != null ? statusMsg : "")); } /* * retrieveValues * ---------------- * XML ドキュメントを解析し、statusCode と statusMsg を取得する。 */ private void retrieveValues(Document doc) { Node n; NodeList nl; // // 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); // STATUS_CODE 属性の値を取得する。 statusCode = ((Element)n).getAttribute("STATUS_CODE"); // // 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; } // // TEXT セクションが存在する場合は、それを取得する。 // n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { // 存在しなくてもエラーではないため、 // このまま戻る。 return; } // 値を取得する。 statusMsg = n.getNodeValue(); } // private 指定のメンバー変数 private String statusCode; private String statusMsg; }