Sun Cluster Entwicklerhandbuch Datendienste für Solaris OS

Anhang G CrnpClient.java-Anwendung

Dieser Anhang enthält die vollständige CrnpClient.java-Anwendung, auf die in Kapitel 12 näher eingegangen wird.

Inhalt von CrnpClient.java

/*
 * CrnpClient.java
 * ================
 *
 * Hinweis zur XML-Analyse:
 *
 * Dieses Programm verwendet die API der Sun Java-Architektur für XML-Verarbeitung (JAXP).
 * Auf http://java.sun.com/xml/jaxp/index.html finden Sie API-Dokumentation und
 * Verfügbarkeitsinformationen.
 *
 * Dieses Programm wurde für Java 1.3.1 oder höher geschrieben.
 *
 * Programmüberblick:
 *
 * Der Haupt-Thread des Programms erstellt ein CrnpClient-Objekt, wartet, bis der
 * Benutzer die Demo beendet hat, und ruft das Herunterfahren für das CrnpClient-
 * Objekt auf und beendet das Programm.
 *
 * Der CrnpClient-Konstruktor erstellt ein EventReceptionThread-Objekt,
 * stellt eine Verbindung zum CRNP-Server her, wobei er den an der Befehlszeile
 * angegebenen Host und Port verwendet, erstellt eine Registrierungsmeldung
 * basierend auf den Befehlszeilenspezifikationen, sendet die
 * Registrierungsmeldung und liest und analysiert die Antwort.
 *
 * Der EventReceptionThread erstellt ein Socket zum Abhören, das an den
 * Hostnamen des Rechners gebunden ist, auf dem dieses Programm ausgeführt wird, sowie
 * an den Port, der an der Befehlszeile angegeben wird. Er wartet auf eine eingehende
 * Ereignisrückmeldung. Zu diesem Zeitpunkt erstellt er ein XML-Dokument aus dem
 * eingehenden Socket-Strom, der dann zur Verarbeitung an das CrnpClient-Objekt
 * zurückgegeben wird.
 *
 * Die Methode für das Herunterfahren im CrnpClient sendet einfach eine Deregistrierungsmeldung
 * (REMOVE_CLIENT) SC_CALLBACK_REG an den CRNP-Server.
 *
 * Hinweis zur Fehlerbehandlung: Einfachheitshalber wird dieses Programm bei den
 * meisten Fehlern einfach beendet. Natürlich würde eine reale Anwendung bei einigen
 * Fehlern andere Lösungen suchen, wie z. B. gegebenenfalls ein erneuter Versuch.
 */

// JAXP-Pakete
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.*;

// Standard-Pakete
import java.net.*;
import java.io.*;
import java.util.*;

/*
 * Klasse CrnpClient
 * -----------------
 * Siehe die obigen Dateiheader-Kommentare.
 */
class CrnpClient
{
	/*
	 * main
	 * ----
	 * Als Eingangspunkt der Ausführung überprüft main lediglich die
	 * Anzahl der Befehlszeilenargumente und erstellt eine Instanz
	 * eines CrnpClient für alle Aufgaben.
	 */
	public static void main(String []args)
	{
		InetAddress regIp = null;
		int regPort = 0, localPort = 0;

		/* Anzahl der Befehlszeilenargumente überprüfen */
		if (args.length < 4) {
			System.out.println(
			    "Syntax: java CrnpClient crnpHost crnpPort "
			    + "lokalerPort (-ac | -ae | -re) "
			    + "[(M | A | RG=Name | R=Name) [...]]");
			System.exit(1);
		}


		/*
		 * Die Befehlszeile soll IP/Port des CRNP-Servers, den
		 * lokalen abzuhörenden Port und die Argumente zur
		 * Angabe des Registrierungstyps enthalten.
		 */
		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 erstellen
		CrnpClient client = new CrnpClient(regIp, regPort, localPort,
		    args);

		// Warten, bis der Benutzer das Programm beenden möchte
		System.out.println("Drücken Sie die Eingabetaste, um die Demo zu beenden...");

		// Der Lesevorgang wird blockiert, bis der Benutzer etwas eingibt
		try {
			System.in.read();
		} catch (IOException e) {
			System.out.println(e.toString());
		}

		// Client herunterfahren
		client.shutdown();
		System.exit(0);
	}

	/*
	 * ======================
	 * Öffentliche Methoden
	 * ======================
	 */

	/*
	 * CrnpClient-Konstruktor
	 * -----------------------
	 * Analysiert die Befehlszeilenargumente, damit der Benutzer weiß, wie die
	 * Verbindung zum CRNP-Server hergestellt werden muss, erstellt den
	 * Ereignisempfangs-Thread und startet dessen Ausführung, erstellt das
	 * XML DocumentBuilderFactory-Objekt und registriert sich
	 * für Rückmeldungen beim CRNP-Server.
	 */
	public CrnpClient(InetAddress regIpIn, int regPortIn, int localPortIn,
	    String []clArgs)
	{
		try {

			regIp = regIpIn;
			regPort = regPortIn;
			localPort = localPortIn;
			regs = clArgs;

			/*
			 * Document Builder Factory für die
			 * XML-Verarbeitung einrichten.
			 */
			setupXmlProcessing();

			/*
			 * EventReceptionThread erstellen, der ein
			 * ServerSocket erstellt und an ein lokales IP und Port bindet.
			 */
			createEvtRecepThr();

			/*
			 * Beim CRNP-Server registrieren.
			 */
			registerCallbacks();

		} catch (Exception e) {
			System.out.println(e.toString());
			System.exit(1);
		}
	}

	/*
	 * processEvent
	 * ---------------
	 * Rückmeldung an CrnpClient, verwendet von EventReceptionThread
	 * beim Empfangen von Ereignisrückmeldungen.
	 */
	public void processEvent(Event event)
	{
		/*
		 * Zu Demonstrationszwecken wird das Ereignis einfach an
		 * System.out gedruckt. Eine reale Anwendung würde das
		 * Ereignis zu einem bestimmten Zweck nutzen.
		 */
		event.print(System.out);
	}

	/*
	 * shutdown
	 * -------------
	 * Vom CRNP-Server deregistrieren.
	 */
	public void shutdown()
	{
		try {
			/* Deregistrierungsmeldung an den Server senden */
			unregister();
		} catch (Exception e) {
			System.out.println(e);
			System.exit(1);
		}
	}


	/*
	 * ======================
	 * Private Helper-Methoden
	 * ======================
	 */

	/*
	 * SetupXmlProcessing
	 * --------------------
	 * Erstellt Document Builder Factory zum
	 * Analysieren der XML-Antworten und -Ereignisse.
	 */
	private void setupXmlProcessing() throws Exception
	{
		dbf = DocumentBuilderFactory.newInstance();

		// Eine Validierung ist nicht erforderlich.
		dbf.setValidating(false);
		dbf.setExpandEntityReferences(false);

		// Kommentare und Leerzeichen sollen ignoriert werden
		dbf.setIgnoringComments(true);
		dbf.setIgnoringElementContentWhitespace(true);

		// CDATA-Abschnitte mit TEXT-Knoten verbinden.
		dbf.setCoalescing(true);
	}

	/*
	 * createEvtRecepThr
	 * -------------------
	 * Erstellt ein neues EventReceptionThread-Objekt, speichert IP und Port,
	 * an die das Abhör-Socket gebunden ist, und startet die
	 * Ausführung des Threads.
	 */
	private void createEvtRecepThr() throws Exception
	{
		/* Thread-Objekt erstellen */
		evtThr = new EventReceptionThread(this);

		/*
		 * Jetzt Ausführung des Threads starten,
		 * um mit dem Abhören für Ereigniszustellungsrückmeldungen
		 * zu beginnen.
		 */
		evtThr.start();
	}


	/*
	 * registerCallbacks
	 * ------------------
	 * Erstellt eine Socketverbindung zum CRNP-Server und sendet
	 * eine Ereignisregistrierungsmeldung.
	 */
	private void registerCallbacks() throws Exception
	{
		System.out.println("Registrierung wird eingeleitet");

		/*
		 * Socketverbindung zum Registrierungs-IP/Port des CRNP-
		 * -Servers erstellen und Registrierungsinformationen senden.
		 */
		Socket sock = new Socket(regIp, regPort);
		String xmlStr = createRegistrationString();
		PrintStream ps = new PrintStream(sock.getOutputStream());
		ps.print(xmlStr);

		/*
		 * Antwort lesen
		 */
		readRegistrationReply(sock.getInputStream());

		/*
		 * Socketverbindung beenden.
		 */
		sock.close();
	}

	/*
	 * unregister
	 * ----------
	 * Genau wie bei registerCallbacks wird eine Socketverbindung zum
	 * CRNP-Server hergestellt, die Deregistrierungsmeldung gesendet, auf die
	 * Antwort vom Server gewartet und das Socket geschlossen.
	 */
	private void unregister() throws Exception
	{
		System.out.println("Deregistrierung wird eingeleitet");

		/*
		 * Socketverbindung zum Registrierungs-IP/Port des CRNP-
		 * Servers erstellen und Deregistrierungsinformationen senden.
		 */
		Socket sock = new Socket(regIp, regPort);
		String xmlStr = createUnregistrationString();
		PrintStream ps = new PrintStream(sock.getOutputStream());
		ps.print(xmlStr);

		/*
		 * Antwort lesen
		 */
		readRegistrationReply(sock.getInputStream());

		/*
		 * Socketverbindung trennen.
		 */
		sock.close();
	}

	/*
	 * createRegistrationString
	 * ------------------
	 * Erstellt ein CallbackReg-Objekt basierend auf den Befehlszeilenargumenten
	 * für dieses Programm und ruft dann die XML-Zeichenkette aus dem
	 * CallbackReg-Objekt ab.
	 */
	private String createRegistrationString() throws Exception
	{
		/*
		 * CallbackReg-Klasse erstellen und Port einrichten.
		 */
		CallbackReg cbReg = new CallbackReg();
		cbReg.setPort("" + localPort);

		// Registrierungstyp einstellen
		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("Ungültiger Reg.typ: " + regs[3]);
			System.exit(1);
		}

		// Ereignisse hinzufügen
		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
	 * ----------------
	 * Erstellt ein XML-Registrierungsereignis mit Klasse EC_Cluster und ohne
	 * Unterklasse.
	 */
	private Event createAllEvent()
	{
		Event allEvent = new Event();
		allEvent.setClass("EC_Cluster");
		return (allEvent);
	}

	/*
	 * createMembershipEvent
	 * ----------------------
	 * Erstellt ein XML-Registrierungsereignis mit Klasse EC_Cluster, Unterklasse
	 * ESC_cluster_memberhip.
	 */
	private Event createMembershipEvent()
	{
		Event membershipEvent = new Event();
		membershipEvent.setClass("EC_Cluster");
		membershipEvent.setSubclass("ESC_cluster_membership");
		return (membershipEvent);
	}

	/*
	 * createRgEvent
	 * ----------------
	 * Erstellt ein XML-Registrierungsereignis mit Klasse EC_Cluster,
	 * Unterklasse ESC_cluster_rg_state und einem "rg_name"-NW-Paar
	 * (basierend auf Eingabeparameter).
	 */
	private Event createRgEvent(String rgname)
	{
		/*
		 * Ein Ressourcengruppen-Zustandsänderungsereignis wird für die
		 * rgname-Ressourcengruppe erstellt. Beachten
		 * Sie, dass für diesen Ereignistyp ein Namens-/Wertepaar (NW-Paar) bereitgestellt
		 * wird, um anzugeben, an welcher Ressourcegruppe Interesse besteht.
		 */
		/*
		 * Ereignisobjekt erstellen und Klasse und Unterklasse einstellen.
		 */
		Event rgStateEvent = new Event();
		rgStateEvent.setClass("EC_Cluster");
		rgStateEvent.setSubclass("ESC_cluster_rg_state");

		/*
		 *NW-Paar-Objekt erstellen und zum Ereignis hinzufügen.
		 */
		NVPair rgNvpair = new NVPair();
		rgNvpair.setName("rg_name");
		rgNvpair.setValue(rgname);
		rgStateEvent.addNvpair(rgNvpair);

		return (rgStateEvent);
	}

	/*
	 * createREvent
	 * ----------------
	 * Erstellt ein XML-Registrierungsereignis mit Klasse EC_Cluster,
	 * Unterklasse ESC_cluster_r_state und einem "r_name"-NW-Paar
	 * (basierend auf Eingabeparameter).
	 */
	private Event createREvent(String rname)
	{
		/*
		 * Ein Ressourcenzustandsereignis wird für die
		 * rgname-Ressource erstellt. Für
		 * diesen Ereignistyp wird ein Namens-/Wertepaar (NW-Paar) bereitgestellt,
		 * um anzugeben, an welcher Ressourcengruppe Interesse besteht.
		 */
		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
	 * ------------------
	 * Erstellt ein REMOVE_CLIENT CallbackReg-Objekt und ruft dann die
	 * XML-Zeichenkette aus dem CallbackReg-Objekt ab.
	 */
	private String createUnregistrationString() throws Exception
	{
		/*
		 * CallbackReg-Objekt erstellen.
		 */
		CallbackReg cbReg = new CallbackReg();
		cbReg.setPort("" + localPort);
		cbReg.setRegType(CallbackReg.REMOVE_CLIENT);

		/*
		 * Die Registrierung wird zum OutputStream geleitet
		 */
		String xmlStr = cbReg.convertToXml();

		// Zeichenkette zur Fehlerbehebung drucken
		System.out.println(xmlStr);
		return (xmlStr);
	}


	/*
	 * readRegistrationReply
	 * ------------------------
	 * Analysiert XML in einem Dokument, erstellt ein RegReply-Objekt
	 * basierend auf dem Dokument und druckt das RegReply-Objekt.
	 * Beachten Sie, dass eine echte Anwendung Aktionen basierend auf dem
	 * Status-Code des RegReply-Objekts ausführen würde.
	 */
	private void readRegistrationReply(InputStream stream)
	    throws Exception
	{
		// Document Builder erstellen
		DocumentBuilder db = dbf.newDocumentBuilder();

		//
		// Vor Analyse ErrorHandler einrichten
		// Standard-Handler verwenden.
		//
		db.setErrorHandler(new DefaultHandler());

		//Eingabedatei analysieren
		Document doc = db.parse(stream);

		RegReply reply = new RegReply(doc);
		reply.print(System.out);
	}

	/* Private Mitgliedsvariablen */
	private InetAddress regIp;
	private int regPort;
	private EventReceptionThread evtThr;
	private String regs[];

	/* Öffentliche Mitgliedsvariablen */
	public int localPort;
	public DocumentBuilderFactory dbf;
}

/*
 * class EventReceptionThread
 * ----------------------------
 * Siehe die obigen Dateiheader-Kommentare.
 */
class EventReceptionThread extends Thread
{
	/*
	 * EventReceptionThread-Konstruktor
	 * ----------------------------------
	 * Erstellt ein neues ServerSocket, gebunden an den lokalen
	 * Hostnamen und einen Platzhalter-Port.
	 */
	public EventReceptionThread(CrnpClient clientIn) throws IOException
	{
		/*
		 * Verweis auf Client festhalten, damit bei Erhalt
		 * eines Ereignisses eine Rückmeldung erfolgen kann.
		 */
		client = clientIn;

		/*
		 * IP angeben, an die gebunden werden soll. Dies ist
		 * die lokale Host-IP. Wenn mehr als eine öffentliche
		 * Schnittstelle auf diesem Rechner konfiguriert ist, wird
		 * diejenige verwendet, die von
		 * InetAddress.getLocalHost angegeben wird.
		 *
		 */
		listeningSock = new ServerSocket(client.localPort, 50,
		    InetAddress.getLocalHost());
    		System.out.println(listeningSock);
	}

	/*
	 * run
	 * ---
	 * Aufgerufen durch die Thread.Start-Methode.
	 *
	 * Bildet eine Endlosschleife und wartet auf eingehende Verbindungen auf
         * dem ServerSocket.
         *
	 * Bei Annahme der einzelnen eingehenden Verbindungen wird jeweils aus dem
	 * XML-Strom ein Ereignisobjekt erstellt, das an das CrnpClient-Objekt zur
	 * Verarbeitung zurückgegeben wird.
	 */
	public void run()
	{
		/*
		 * Endlosschleife.
		 */
		try {
			//
			// Document Builder unter Verwendung der Document
			// Builder Factory im CrnpClient erstellen.
			//
			DocumentBuilder db = client.dbf.newDocumentBuilder();

			//
			// Vor der Analyse  ErrorHandler einrichten
			// Standard-Handler verwenden.
			//
			db.setErrorHandler(new DefaultHandler());

			while(true) {
				/* Auf Rückmeldung vom Server warten */
				Socket sock = listeningSock.accept();

				// Eingabedatei analysieren
				Document doc = db.parse(sock.getInputStream());

				Event event = new Event(doc);
				client.processEvent(event);

				/* Socket schließen */
				sock.close();
			}
			// NICHT ERREICHBAR

		} catch (Exception e) {
			System.out.println(e);
			System.exit(1);
		}
	}

	/* private Mitgliedsvariablen */
	private ServerSocket listeningSock;
	private CrnpClient client;
}

/*
 * Klasse NVPair
 * -----------
 * Diese Klasse speichert ein Namens-/Wertepaar (beides Zeichenketten). Sie kann
 * eine NVPAIR XML-Meldung aus den Mitgliedern erstellen und ein
 * NVPAIR XML-Element in den Mitgliedern analysieren
 *
 * Beachten Sie, dass die formale Spezifikation eines NVPAIR mehrere Werte zulässt.
 * Der Einfachheit halber wird hier angenommen, dass nur ein Wert zulässig ist.
 */
class NVPair
{
	/*
	 * Zwei Konstruktoren: Der erste erstellt ein leeres NVPair, der zweite
	 * erstellt ein NVPair aus einem NVPAIR XML-Element.
	 */
	public NVPair()
	{
		name = value = null;
	}

	public NVPair(Element elem)
	{
		retrieveValues(elem);
	}

	/*
	 * Öffentliche Setter.
	 */
	public void setName(String nameIn)
	{
		name = nameIn;
	}

	public void setValue(String valueIn)
	{
		value = valueIn;
	}

	/*
	 * Druckt den Namen und Wert in einer einzigen Zeile.
	 */
	public void print(PrintStream out)
	{
		out.println("NAME=" + name + " VALUE=" + value);
	}

	/*
	 * createXmlElement
	 * ------------------
	 * Erstellt ein NVPAIR XML-Element aus den Mitgliedsvariablen.
	 * Nimmt das Dokument als Parameter, um das Element
	 * erstellen zu können.
	 */
	public Element createXmlElement(Document doc)
	{
		// Element erstellen.
		Element nvpair = (Element)
		    doc.createElement("NVPAIR");
		//
		// Namen hinzufügen. Beachten Sie, dass der tatsächliche
		// Name ein eigener CDATA-Abschnitt ist.
		//
		Element eName = doc.createElement("NAME");
		Node nameData = doc.createCDATASection(name);
		eName.appendChild(nameData);
		nvpair.appendChild(eName);
		//
		// Wert hinzufügen. Beachten Sie, dass der tatsächliche
		// Wert ein eigener CDATA-Abschnitt ist.
		//
		Element eValue = doc.createElement("VALUE");
		Node valueData = doc.createCDATASection(value);
		eValue.appendChild(valueData);
		nvpair.appendChild(eValue);

		return (nvpair);
	}

	/*
	 * retrieveValues
	 * ----------------
	 * Analysiert das XML-Element, um Namen und Wert abzurufen.
	 */
	private void retrieveValues(Element elem)
	{
		Node n;
		NodeList nl;

		//
		// NAME-Element suchen
		//
		nl = elem.getElementsByTagName("NAME");
		if (nl.getLength() != 1) {
			System.out.println("Analysefehler: "
			    + "NAME-Knoten kann nicht gefunden werden.");
			return;
		}

		//
		// TEXT-Abschnitt abrufen
		//
		n = nl.item(0).getFirstChild();
		if (n == null || n.getNodeType() != Node.TEXT_NODE) {
			System.out.println("Analysefehler:"
			    + "TEXT-Abschnitt kann nicht gefunden werden.");
			return;
		}

		// Wert abrufen
		name = n.getNodeValue();

		//
		// Jetzt Wertelement abrufen
		//
		nl = elem.getElementsByTagName("VALUE");
		if (nl.getLength() != 1) {
			System.out.println("Analysefehler: "
			    + "VALUE-Knoten kann nicht gefunden werden.");
			return;
		}

		//
		// TEXT-Abschnitt abrufen
		//
		n = nl.item(0).getFirstChild();
		if (n == null || n.getNodeType() != Node.TEXT_NODE) {
			System.out.println("Analysefehler: "
			    + "TEXT-Abschnitt kann nicht gefunden werden.");
			return;
		}

		// Wert abrufen
		value = n.getNodeValue();
	}


	/*
	 * Öffentlicher Zugang
	 */
	public String getName()
	{
		return (name);
	}

	public String getValue()
	{
		return (value);
	}

	// Private Mitgliedsvariablen
	private String name, value;
}


/*
 * Klasse Event
 * -----------
 * Diese Klasse speichert ein Ereignis, das aus Klasse, Unterklasse, Anbieter,
 * Herausgeber und einer Liste der Namens-/Wertepaare besteht. Sie kann ein
 * SC_EVENT_REG XML-Element aus den Mitgliedern erstellen und ein
 * SC_EVENT XML-Element in den Mitgliedern analysieren. Beachten Sie die Assymetrie:
 * SC_EVENT-Elemente werden analysiert, SC_EVENT_REG-Elemente dagegen erstellt.
 * Das liegt daran, dass SC_EVENT_REG-Elemente in Registrierungsmeldungen verwendet
 * werden, die erstellt werden müssen, während die SC_EVENT-Elemente für
 * Ereigniszustellungen verwendet werden, die zu analysieren sind. Der einzige Unterschied
 * besteht darin, dass SC_EVENT_REG-Elemente keinen Anbieter oder Herausgeber haben.
 */
class Event
{

	/*
	 * Zwei Konstruktoren; Der erste erstellt ein leeres Ereignis, der zweite
	 * erstellt ein Ereignis anhand eines SC_EVENT XML-Dokuments.
	 */
	public Event()
	{
		regClass = regSubclass = null;
		nvpairs = new Vector();
	}

	public Event(Document doc)
	{

		nvpairs = new Vector();

		//
		// Dokument für Fehlerbehebungszwecke in eine
		// Zeichenkette konvertieren.
		//
		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());

		// Analyse ausführen.
		retrieveValues(doc);
	}

	/*
	 *  Öffentliche Setter.
	 */
	public void setClass(String classIn)
	{
		regClass = classIn;
	}

	public void setSubclass(String subclassIn)
	{
		regSubclass = subclassIn;
	}

	public void addNvpair(NVPair nvpair)
	{
		nvpairs.add(nvpair);
	}

	/*
	 * createXmlElement
	 * ------------------
	 * Erstellt ein SC_EVENT_REG XML-Element aus den Mitgliedsvariablen.
	 * Verwendet das Dokument als Parameter, um das Element zu
	 * erstellen. Beruht auf der createXmlElement-Fähigkeit des NW-Paars.
	 */
	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);
	}

	/*
	 * Druckt die Mitgliedsvariablen in mehreren Zeilen.
	 */
	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
	 * ----------------
	 * Analysiert XML-Dokument, um Klasse, Unterklasse, Hersteller,
	 * Herausgeber und NW-Paare abzurufen.
	 */
	private void retrieveValues(Document doc)
	{
		Node n;
		NodeList nl;

		//
		// SC_EVENT-Element suchen.
		//
		nl = doc.getElementsByTagName("SC_EVENT");
		if (nl.getLength() != 1) {
			System.out.println("Analysefehler "
			    + "SC_EVENT-Knoten kann nicht gefunden werden.");
			return;
		}

		n = nl.item(0);

		//
		// Werte der Attribute CLASS, SUBCLASS,
		// VENDOR und PUBLISHER abrufen.
		//
		regClass = ((Element)n).getAttribute("CLASS");
		regSubclass = ((Element)n).getAttribute("SUBCLASS");
		publisher = ((Element)n).getAttribute("PUBLISHER");
		vendor = ((Element)n).getAttribute("VENDOR");

		//
		// Alle NW-Paare abrufen
		//
		for (Node child = n.getFirstChild(); child != null;
		     child = child.getNextSibling())
		{
			nvpairs.add(new NVPair((Element)child));
		}
	}

	/*
	 * Öffentliche Zugangsmethoden.
	 */
	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 Mitgliedsvariablen.
	private String regClass, regSubclass;
	private Vector nvpairs;
	private String vendor, publisher;
}


/*
 * class CallbackReg
 * -----------
 * Diese Klasse speichert einen Port und einen Reg.typ (beides Zeichenketten) sowie eine
 * Ereignisliste. Sie kann eine SC_CALLBACK_REG XML-Meldung anhand ihrer Mitglieder erstellen.
 *
 * Beachten Sie, dass diese Klasse keine SC_CALLBACK_REG-Meldungen analysieren können muss,
 * da nur der CRNP-Server SC_CALLBACK_REG-Meldungen analysieren muss.
 */
class CallbackReg
{
	// Nützliche Definitionen für die setRegType-Methode
	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();
	}

	/*
	 * Öffentliche Setter.
	 */
	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("Fehler, ungültiger regType " +
			    regTypeIn);
			regType = "ADD_CLIENT";
			break;
		}
	}

	public void addRegEvent(Event regEvent)
	{
		regEvents.add(regEvent);
	}


	/*
	 * convertToXml
	 * ------------------
	 * Erstellt ein SC_CALLBACK_REG XML-Dokument anhand der
	 * Mitgliedsvariablen. Beruht auf der createXmlElement-Fähigkeit
	 * des Ereignisses.
	 */
	public String convertToXml()
	{
		Document document = null;
		DocumentBuilderFactory factory =
		    DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.newDocument();
		} catch (ParserConfigurationException pce) {
			// Analysierer mit den angegebenen Optionen kann nicht erstellt werden
			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);

		//
		// Jetzt Dokument in eine Zeichenkette konvertieren.
		//
		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 Mitgliedsvariablen
	private String port;
	private String regType;
	private Vector regEvents;
}

/*
 * class RegReply
 * -----------
 * Diese Klasse speichert einen status_code und status_msg (beides Zeichenketten).
 * Sie kann ein SC_REPLY XML-Element mitsamt seinen Mitgliedern analysieren.
 */
class RegReply
{

	/*
	 * Der einzige Konstruktor übernimmt ein XML-Dokument und analysiert es.
	 */
	public RegReply(Document doc)
	{
		//
		// Jetzt Dokument in eine Zeichenkette konvertieren.
		//
		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);
	}

	/*
	 * Öffentlicher Zugang
	 */
	public String getStatusCode()
	{
		return (statusCode);
	}

	public String getStatusMsg()
	{
		return (statusMsg);
	}

	/*
	 * Druckt die Informationen in einer einzigen Zeile.
	 */
	public void print(PrintStream out)
	{
		out.println(statusCode + ": " +
		    (statusMsg != null ? statusMsg : ""));
	}

 	/*
	 * retrieveValues
	 * ----------------
	 * Analysiert das XML-Dokument, um statusCode und statusMsg abzurufen.
	 */
	private void retrieveValues(Document doc)
	{
		Node n;
		NodeList nl;

		//
		// SC_REPLY-Element suchen.
		//
		nl = doc.getElementsByTagName("SC_REPLY");
		if (nl.getLength() != 1) {
			System.out.println("Analysefehler: "
			    + "SC_REPLY-Knoten kann nicht gefunden werden.");
			return;
		}

		n = nl.item(0);

		// Wert des STATUS_CODE-Attributs abrufen
		statusCode = ((Element)n).getAttribute("STATUS_CODE");

		//
		// SC_STATUS_MSG-Element suchen
		//
		nl = ((Element)n).getElementsByTagName("SC_STATUS_MSG");
		if (nl.getLength() != 1) {
			System.out.println("Analysefehler"
			    + "SC_STATUS_MSG-Knoten kann nicht gefunden werden.");
			return;
		}

		//
		// TEXT-Abschnitt abrufen, falls vorhanden.
		//
		n = nl.item(0).getFirstChild();
		if (n == null || n.getNodeType() != Node.TEXT_NODE) {
			// Kein Fehler, falls nicht vorhanden; einfach
			// stillschweigend zurückkehren.
			return;
		}

		// Wert abrufen
		statusMsg = n.getNodeValue();
	}

	// private Mitgliedsvariablen
	private String statusCode;
	private String statusMsg;
}