Der vorliegende Text beschreibt die Installation und die wichtisten Funktionen und Klassen der API (Application Programming Interface) für Remote Enscribe in Java. Remote Enscribe ist ein Paket zum transparenten Zugriff auf HP-NonStop-Dateien und Datenbanken. Remote Enscribe ist auch in C verfügbar.
Beachten Sie auch das Parallelprodukt Remote SQL!
Die vorliegende Software Remote Enscribe stellt eine Schnittstelle zwischen beliebigen Java-Clients und dem HP-NonStop-Dateisystem ENSCRIBE her. Sie ist als Client/Server-Paar implementiert, wobei der Client zu 100% in Java realisiert ist. In der vorliegenden Fassung werden folgende Funktionen unterstützt:
Die API ist der C-API sehr ähnlich. Es ist geplant, einige
Funktionen zu erweitern, damit sie stärker objektorientiert
arbeiten. So liefern die Dateizugriffsfunktionen bei Fehlern einen
Returnwert zurück, statt eine Exception zu erzeugen (das
Verhalten kann allerdings mit Host.throwException(true)
beeinflusst werden.) Auch wird java.io.OutputStream noch nicht
unterstützt.
Die Kommunikation erfolgt über einen Prozess auf dem NonStop-System, der die TCP/IP-Nachrichten in Aufrufe an das Dateisystem weitergibt. Der TCP/IP-LISTNER startet RSQLSRV bei jeder Kommunikationsanforderung automatisch. Der Server wird identisch auch für Remote SQL eingesetzt, für das jetzt auch eine Java-API existiert.
Der Server RSQLSRV muß auf dem NonStop-System installiert werden. Dazu ist eine Zeile in der Datei PORTCONF der TCP/IP-Konfiguration für den LISTNER einzutragen:
741 $system.rsql.rsqlsrv -p150
Danach ist der LISTNER-Prozess neu zu starten.
Das ausführbare Programm muß im Binärformat auf
das NonStop-System, hier im Subvolume $SYSTEM.RSQL
kopiert werden und den Filecode 100 erhalten. Soll der Server auch
für Remote-SQL-Zugriffe
eingesetzt werden, so ist er mit SQLCOMP
noch in einen
beliebigen SQL-Katalog einzutragen.
Die Portnummer, hier 741, kann frei gewählt werden. Der angegebene Wert ist der Default.
Der Parameter -p150
legt die Priorität des
Prozesses auf 150 fest. Andere Werte sind jederzeit möglich. Die
korrekte Wahl der Priorität ist eine Maßnahme, um unnötige
Systembelastungen durch komplexe Abfragen auf eine Minimum zu
reduzieren.
Die Java-Klassen sind im Archiv renscribe.jar gespeichert, das dem Klassenpfad hinzugefügt werden muß. Innerhalb einer IDE wird dies normalerweise im Projekt eingetragen. Auf aktuellen Java-Plattformen, kann der Aufruf mit
java -cp renscribe.jar MyClass
erfolgen. Geben sie den vollständigen Pfad der Jar-Datei an.
Sie können die Datei auch dem System-Klassenpfad, je nach
System in der Systemsteuerung (NT, W2K, XP), CONFIG.SYS (OS/2),
AUTOEXEC.BAT (W9x) oder dem Benutzerprofil (Unix) hinzufügen.
Java ab Version 1.2 unterstützt auch die Speicherung des
Jar-Files im Verzeichnis jre\lib\ext
unterhalb des
Java-Verzeichisses.
Die vollständige API-Dokumentation liegt im Javadoc-Format vor: Remote Enscribe Java API.
Für die folgenden Funktionen müssen sie das Package de.mvcsys.renscribe.* importieren.
Zum Aufbau der Verbindung und speichern der Verbindungsdaten legen sie ein de.mvcsys.renscribe.Host-Objekt an:
Host host;
try {
host = new Host( "hpns", 741, "USER.NAME", "geheim" );
} catch( Exception e ) {
System.err.println( "Verbindunsaufbau gescheitert: "
+ e.toString() );
}
Sollte die Verbindung abbrechen, muß sie neu erstellt werden, da auch alle offenen Dateien geschlossen werden. Die Verbindung kann jederzeit mit der disconnect-Methode vorzeitig abgebaut werden.
Verbindungen können auch unter Verwendung der Klasse HostPool verwaltet werden.
Eine Enscribe-Datei (oder auch einen Prozess oder ein Device) öffnen Sie, indem Sie vom beim Verbindungsaufbau erzeugten Host-Objekt host mit open ein de.mvcsys.renscribe.EFile-Objekt anlegen lassen:
EFile file = host.open( "$DATA.DBFILES.CUSTOMER",
"r+",
Host.OPEN_SHARED | Host.OPEN_BUFFERED,
130 );
if ( file == null ) {
System.err.println( "Fehler " + host.errno + " bei open: "
+ host.errorMsg );
}
Auf die so geöffnete Datei können Sie jetzt mit allen Methoden der Klasse EFile zugreifen.
Sie können Transaktionen auf dem NonStop-System mit der Methode transact der Klasse Host steuern. Die Parameter Host.TT_BEGIN, Host.TT_COMMIT oder Host.TT_ROLLBACK steuern, ob die Transaktion begonnen, beendet oder abgebrochen wird.
Defines der Form "=NAME" können mit der Methode loadDefines der Klasse Host gesetzt werden. Das Argument ist ein Befehl aus folgender Liste:
DELETE DEFINE ** |
Alle Defines löschen. |
DELETE DEFINE =<name> |
Einzelnes Define löschen. |
ADD DEFINE =<name>, <attribut> <wert>, ... |
Define hinzufügen (oder ändern, wenn es existiert). |
ALTER DEFINE =<name>, <attribut> <wert>, ... |
Define ändern. |
[OBEY] <Dateiname> |
Die Befehle in <Dateiname> auf dem Host werden ausgeführt. Sie müssen der obigen Syntax folgen. Der Befehl OBEY kann auch weggelassen werden. OBEY kann auch rekursiv in der Datei vorkommen. |
LOADINFO <Dateiname> |
Analysiere die Ausgabe des TACL-Befehls |
TACL <Befehl> |
Der Befehl wird (via system) ausgeführt. Danach wird ein INFO DEFINE Befehl abgesetzt und dessen Ausgabe wie oben analysiert. Das erlaubt zum Beispiel die Verwendung von TACL-Segment-Files. |
Die Methoden zum Öffnen und Lesen von Dateien erlauben die
Verwendung von zwei Pseudo-Dateien: $DEFINES$
zum Lesen
der aktuell gesetzten Defines und $FILES$
zum Lesen von
Datei- oder Prozessnamen. Die Zeilen sind mit Linefeed getrennt.
Datei |
Beispielausgabe (1. Zeile ist Kommentar) |
---|---|
|
<----- Name (24) ------> <Klasse> <Attri> <----------- Wert (34) ----------> =CTYPEH MAP FILE \GZE.$SYSTEM.SYSTEM.CTYPEH =GNSCTLG CATALOG SUBVOL \GZE.$DEV06.GNSCTLGL |
|
<----------- Name (34) ----------> DevTyp SubDev ObjTyp FilTyp FilCod \GZE.$DEV03.CUBERSQL.TESTFILE 3 39 0 2 7890 \GZE.$DEV03.CUBERSQL.TESTIN 3 39 0 0 101 \GZE.$DEV03.CUBERSQL.TESTTAB 3 39 2 3 0 |
Die Informationen können mit Hilfe der seek-Methode eingeschränkt werden.
Datei |
Key-Specifier |
Bedeutung |
---|---|---|
|
Host.DEFINES_KEY_NAME |
Der Define-Name muss mit dem Key anfangen. |
|
Host.DEFINES_KEY_CLASS |
Der CLASS-Name muss mit dem Key anfangen. |
|
Host.DEFINES_KEY_ATTR |
Der Attribut-Name muss mit dem Key anfangen. |
|
Host.DEFINES_KEY_VALUE |
Der Attribut-Wert muss mit dem Key anfangen. |
|
0 |
Der Key hat mehrere Felder, die durch Space, Tab oder die folgenden Zeichen getrennt werden: ",;:".
Ein "*" setzt den Default. Parameter am Ende können weggelassen werden. Die Parameter 3 und 4 können mit "~" anfangen, um sie zu negieren. Die Parameter 5 bis 7 erlauben einen Vergleichsoperator: "=~<>". Beispiel: " |
Die Methode system
der Klasse Host
erlaubt die Ausführung beliebiger TACL-Kommandos. Der
Implementierung liegt die Funktion system
der
C-Bibliothek zugrunde. Diese startet beim ersten Aufruf einen
TACL-Prozess mit $RECEIVE
als IN und ohne
OUT-File. Dieser TACL startet weder TACLLOCL
noch
TACLCSTM
, sie können dies aber selber nachholen.
Dem Prozess werden dann alle weiteren system
-Aufrufe
übergeben.
Es bietet sich an, alle Programme mit expliziter OUT-Datei zu starten und diese Datei dann per API zu lesen.
/**
* Test
*/
public static void main( String args[] )
throws Exception
{
byte buffer[] = new byte[ 100 ];
int port = 741;
String pass = "?";
if ( args.length > 0 ) {
pass = args[ 0 ];
}
if ( args.length > 1 ) {
port = Integer.parseInt( args[ 1 ] );
}
try {
/*
* Verbindungsaufbau
*/
Host host = new Host( "gze", port, "qa.cube", pass, true );
host.setThrowException( true );
host.startLog( "$TDD" );
/*
* Datei-Info
*/
System.out.println( host.info( "CUBERSQL.TESTFILE" ) );
System.out.println( host.info( "\\gze.$dev05.gnsdbtt.karte" ) );
System.out.println( host.info( "TACLCSTM" ) );
/*
* Datei lesen
*/
EFile ef = host.open( "TACLCSTM", "r",
Host.OPEN_SHARED | Host.OPEN_BUFFERED, 100 );
if ( ef != null ) {
while ( !ef.isEof ) {
int l = ef.read( buffer, buffer.length, READ_NOLOCK );
if ( l == 0 && !ef.isEof ) {
/*
* Fehler
*/
throw new IOException( host.errorMsg );
}
System.out.print( Host.makeString( buffer, 0, l, host.ENCODING ) );
}
// ef.close();
host.closeAll();
}
/*
* Defines
*/
host.loadDefines( "LOADINFO CUBERSQL.DEFLIST" );
ef = host.open( "$DEFINES$", "r", Host.OPEN_BUFFERED, 80 );
if ( ef != null ) {
ef.seek( "SUB", Host.DEFINES_KEY_ATTR, SEEK_GENERIC );
while ( !ef.isEof ) {
int l = ef.read( buffer, buffer.length, READ_NOLOCK );
if ( l == 0 && !ef.isEof ) {
/*
* Fehler
*/
throw new IOException( host.errorMsg );
}
System.out.print( Host.makeString( buffer, 0, l, host.ENCODING ) );
}
ef.close();
}
/*
* Dateiliste
*/
ef = host.open( "$FILES$", "r", Host.OPEN_BUFFERED, 80 );
if ( ef != null ) {
// ef.seek( "\\GZE.$*.GNSDB?L.* -1 3 * >0", 0, SEEK_GENERIC );
ef.seek( "$DEV03.*CUBE*.* -1 3", 0, SEEK_GENERIC );
while ( !ef.isEof ) {
int l = ef.read( buffer, buffer.length, READ_NOLOCK );
if ( l == 0 && !ef.isEof ) {
/*
* Fehler
*/
throw new IOException( host.errorMsg );
}
System.out.print( Host.makeString( buffer, 0, l, host.ENCODING ) );
}
ef.close();
}
/*
* Datei schreiben
*/
ef = host.open( "TESTFILE", "wt", 0, 100 );
if ( ef != null ) {
int result = ef.write( "Test\n" );
System.out.println( "write - result=" + result );
result = ef.write( "ÄÖÜäöüß\n" );
System.out.println( "write - result=" + result );
ef.close();
}
/*
* TACL
*/
host.system( "#push #out" );
host.system( "#set #out $S.#MVC" );
host.system( "#output Hallo" );
host.system( "FILEINFO" );
host.system( "#pop #out" );
host.disconnect();
}
catch ( Exception e ) {
e.printStackTrace();
}
}