Parse XLS using openpyxl

Kapitel 4 – Remote Function Call mit Java

Michael Wegelin, Michael Englbrecht
SAP-Schnittstellenprogrammierung

In diesem Kapitel lernen Sie unterschiedliche Möglichkeiten kennen, um RFC-Programme in Java zu schreiben. Wir zeigen Ihnen die Verwendung verschiedener Bibliotheken sowohl außerhalb als auch innerhalb von SAP-Infrastrukturen wie dem SAP NetWeaver Application Server Java oder SAP Enterprise Portal.

In Java gibt es unterschiedliche (und unterschiedlich komfortable) Möglichkeiten, um RFC-Programme zu realisieren. Als grundlegende Möglichkeit bietet SAP den SAP Java Connector an, mit dem Sie auch außerhalb von SAP-Servern und ohne SAP-Entwicklungsumgebung programmieren können (siehe Abschnitt 4.1). Komfortabler ist die Entwicklung mit dem SAP Enterprise Connector (siehe Abschnitt 4.2), für den Sie das SAP NetWeaver Developer Studio (NWDS) benötigen. Sie erfahren, wie Sie ab SAP Composition Environment 7.0 EHP 2 die ARFC2-Bibliothek einsetzen können (siehe Abschnitt 4.3, »Nutzung generischer Backend-Modelle«). In Abschnitt 4.4, »RFC-Server«, erklären wir Ihnen die Realisierung von RFC-Servern und gehen im letzten Abschnitt auf die RFC-Programmierung innerhalb des aktuellen Release von SAP Enterprise Portal ein.

4.1 SAP Java Connector

Der SAP Java Connector (JCo) bildet die Schnittstelle zwischen einem Javabasierten System und dem ABAP-Backend. Der Java Connector bietet beide Richtungen der Kommunikation an: Es ist möglich, sowohl von Java-Anwendungen auf ABAP-Funktionen zuzugreifen als auch aus ABAP-Applikationen auf Java-Anwendungen. Wir gehen beim folgenden Szenario davon aus, dass die Entwicklung der Anwendung nicht in einer SAP-Entwicklungsumgebung wie dem SAP NetWeaver Developer Studio erfolgt. Wir verwenden stattdessen die freie Entwicklungsumgebung Eclipse.

4.1.1 Installation

    Die Installation des SAP Java Connectors erfolgt in zwei Schritten:

  • 1. Im ersten Schritt laden Sie den SAP Java Connector vom SAP Service Marketplace herunter. Nachdem Sie sich unter http://service.sap.com connectors mit Ihrem SAP-Service-Marketplace-Benutzer angemeldet haben, navigieren Sie über das Menü zu SAP Java Connector/Tools & Services und klicken auf den Link Download SAP JCo Release 3.0..
    Wählen Sie hier die passende Version für Ihr Windows-Betriebssystem aus. Nach dem Download entpacken Sie die heruntergeladene Datei. Auf Ihrer Festplatte befindet sich nun eine Datei mit dem Namen sapjco3-nitel-3.0.. Entpacken Sie diese Datei ebenfalls. Es befinden sich dann die Dateien sapjco3.jar und sapjco3.dll auf Ihrer Festplatte. Neben
    diesen beiden Laufzeitdateien beinhaltet die ZIP-Datei zusätzlich die JCo-Dokumentation im Javadoc-Format für den Java Connector.

  • 2. Im zweiten Schritt der Installation fügen Sie die Datei sapjco3.jar in den Klassenpfad Ihrer Entwicklungsumgebung ein. Für einen reibungslosen Ablauf wird empfohlen, sowohl die JAR-Datei als auch die Bibliothek sapjco3.dll im selben Verzeichnis bereitzustellen.

Falls Sie den SAP Java Connector unter Linux bzw. Unix verwenden möchten, laden Sie die entsprechende Version für Ihr Betriebssystem vom SAP Service Marketplace herunter. Sie werden in der Datei fast die gleichen Dateien vorfinden wie unter Windows; der Unterschied besteht lediglich in der Bibliothek. Das Pendant zu sapjco3.dll ist unter Linux die Datei libsapjco3.so.

Versionen des SAP Java Connectors

Neben den auf dem SAP Service Marketplace abgelegten Java-Connector-Versionen liefert SAP stets auch eine Version des Connectors über das SAP NetWeaver Developer Studio aus. Unter SAP NetWeaver 7.0 ist es die Connector-Version 2.0, die sich stark von der hier besprochenen Version 3. 0 unterscheidet. Bei Version 3.0 handelt sich um den Extrakt der Java Connector API aus der ab SAP NetWeaver 7.0 EHP 1 angebotenen API. Im SAP-Sprachgebrauch haben die Versionen, die direkt über den SAP Service Marketplace geladen werden können, den Namenszusatz standalone. Diese Standalone-Versionen werden außerhalb der SAP-Entwicklungsumgebung verwendet. Sollten Sie mit dem SAP NetWeaver Developer Studio Anwendungen entwickeln, achten Sie darauf, dass Sie die Version verwenden, die zusammen mit der Entwicklungsumgebung ausgeliefert wird.
Dies ist besonders dann zu beachten, wenn Sie mit der SAP NetWeaver Developer Infrastructure arbeiten. In Version 3.0 wurden nicht nur Fehler beseitigt, sondern auch umfangreiche Änderungen an der API vorgenommen. Diese Änderungen führen so weit, dass beim Umstieg von einer älteren Version des SAP Java Connectors auf Version 3.0 Änderungen an der Software durchgeführt werden müssen. Dies hat einerseits den Nachteil eines gewissen Aufwands, andererseits nähert man sich mit Version 3.0 zum ersten Mal einer API, die den üblichen Programmierkonventionen in Java entspricht.

4.1.2 Architektur des SAP Java Connectors

Da nun die Installation erfolgreich abgeschlossen ist, wendet sich dieser Abschnitt der Verwendung des SAP Java Connectors zu.
Die Architektur des SAP Java Connectors kann prinzipiell in zwei Bereiche unterteilt werden. Zum einen gibt es den reinen Java-Teil, der Ihnen als Entwickler über eine API eine vereinfachte Kommunikation mit dem Backend ermöglicht. Der andere Teil ist ein nativer Layer. Dieser Layer wird über die Dynamic Link Library (DLL) bereitgestellt. Bei der Kommunikation zwischen der Java API und der C-RFC-Bibliothek kommt das Java Native Interface (JNI) zum Einsatz.
Die Kommunikation zwischen JNI und der DLL ist für Sie als Entwickler jedoch irrelevant. Dies hat den Vorteil, dass Sie sich völlig auf den Java-Teil konzentrieren können und sich nicht um die Realisierung in C sorgen müssen. Abbildung 4.1 stellt die Kommunikationsmöglichkeiten und die grundlegende Architektur des SAP Java Connectors vor.


Abbildung 4.1 Architektur des SAP Java Connectors

Der SAP Java Connector unterstützt sowohl die Möglichkeit der eingehenden (inbound-/serverseitigen) als auch die der ausgehenden (outbound-/clientseitigen) Kommunikation. In beide Richtungen werden sowohl die herkömmliche RFC-Kommunikation als auch der transaktionale RFC (tRFC) und der queued RFC (qRFC) unterstützt. Die API ist sehr spartanisch ausgefallen, sodass Sie einige Dinge selbst übernehmen müssen. Dazu gehören zum Beispiel das Zwischenspeichern (Cachen) von Metadaten sowie das Verwalten eindeutiger Verbindungspools.

Noch einmal zurück zur Abarbeitung eines ABAP-Funktionsbausteinaufrufs aus Java: Wie Sie in Abbildung 4.2 sehen können, haben die Entwickler des SAP Java Connectors durchaus darauf geachtet, dass zukünftig möglicherweise weitere Kommunikationsfacetten zwischen dem Java Connector und dem ABAP-Backend denkbar sind.


Abbildung 4.2 Kommunikationsbestandteile bei der Verarbeitung eines RFC-Aufrufs

Aus diesem Grund wurde ein Middleware-Interface eingeführt. Dieses Middleware-Interface stellt die Abstraktionsschicht zwischen der JCo API und der eigentlichen C-RFC-Bibliothek dar. Es ist also durchaus denkbar, dass anstelle des JNI-Aufrufs und der darunterliegenden DLL auch andere Java Kommunikationsmöglichkeiten verwendet werden könnten, wie zum Beispiel SOAP-Aufrufe. Die Integration anderer Kommunikationsarten anstelle der nativen Kommunikation kann jedoch nur durch SAP selbst erfolgen.

4.1.3 Programmierung mit dem SAP Java Connector

Die Programmierung mit dem SAP Java Connector besteht für einen einfachen synchronen RFC-Aufruf grundsätzlich aus drei Schritten. Abbildung 4.3 stellt die relevanten Klassen aus der JCo API und deren Kommunikationsschnittstellen in einem Sequenzdiagramm dar. Wie Sie sehen können, wird das Szenario durch die Java-Klasse JavaClient initiiert. Der Java-Client
kümmert sich im ersten Schritt um den Aufbau der Verbindung zum gewünschten SAP-Backend. Im zweiten Schritt ist es notwendig, einen Funktionsbaustein-Proxy innerhalb des Java-Clients zu erzeugen. Dieser Proxy dient dazu, die Metadaten des gewünschten Funktionsbausteins bereitzustellen. Die Metadaten spielen beim Füllen der Importparameter (Tabellenparameter eingeschlossen) sowie beim Auslesen der Export- und Tabellenparameter eine wichtige Rolle.
Im Folgenden diskutieren wir nun Schritt für Schritt das Sequenzdiagramm aus Abbildung 4.3.


Abbildung 4.3 Abarbeitung eines Funktionsbausteinaufrufs

Verbindungsaufbau

Um eine Verbindung mit einem ABAP-Backend aufzubauen, wird die Klasse JCoDestinationManager verwendet. Die Klasse stellt die Factory-Methode getDestination zur Verfügung, mit der Objekte vom Typ JCoDestination erzeugt werden. Mittels des erzeugten Objekts werden dem Konnektor die Verbindungsinformationen des Backend-Systems mitgeteilt. Die Methode getDestination wird in zwei verschiedenen Ausprägungen bereitgestellt.
Die eine Ausprägung erhält lediglich einen String. Dieser enthält den Namen einer Datei, die die Verbindungsparameter umfasst. Die Datei wird durch den SAP Java Connector jeweils im Basisverzeichnis des Projekts gesucht. Die konkrete Angabe eines Verzeichnisses ist standardmäßig nicht möglich, sondern muss über die Implementierung einer eigenen Destinationsinformations-Provider-Klasse realisiert werden. Als Dateiname wird der Bezeichner für die Destination verwendet, als Dateiendung ist zwingend .jcoDestination erforderlich. Listing 4.1 zeigt die Erzeugung einer JCoDestination-Instanz.

Listing 4.1 Erzeugen eines »JCoDestination«-Objekts

Der Aufbau der Property-Datei folgt den bekannten Regeln, die die Klasse java.util.Property definiert. Dabei müssen Sie nicht alle Schlüssel der Eigenschaften auswendig kennen. Die Eigenschaften, die vom SAP Java Connector erwartet werden, definiert das Interface DestinationDataProvider als Konstanten. Listing 4.2 stellt den Aufbau einer einfachen Konfiguration dar. Die Eigenschaften führen alle das Präfix jco.client und sind selbsterklärend.

Listing 4.2 Verbindungsdefinition der Destination »MYDESTINATION.jcoDestination«

In vielen Fällen ist es jedoch nicht sinnvoll und schon gar nicht möglich, Verbindungsdaten unflexibel bei den Binärdateien von Java abzulegen. Im Normalfall möchten Sie über den Ablageort der Verbindungsparameter flexibel bestimmen. Zu diesem Zweck wurde das Interface Destination-DataProvider eingeführt. Sollten Sie eine andere Art der Konfiguration Ihrer JCo-Verbindungsdaten wünschen, implementieren Sie einfach dieses Interface.
Das Interface definiert die drei Methoden getDestinationProperties, supportEvents und setDestinationDataEventListener.
Zusätzlich zur Methode getDestination in der Ausprägung mit nur einem String, der nur der Destinationsbezeichner übergeben wird, wird noch eine weitere Ausprägung angeboten. Diese erhält neben dem Bezeichner der Destination einen zusätzlichen Parameter, den die Java-Dokumentation mit dem Namen scopeType beschreibt. Dieser Parameter wird nicht zwingend ausgewertet. Er wird nur dann verarbeitet, wenn eine darunterliegende Infrastruktur für die Verwaltung der Sitzungen angeboten wird. Die Nutzung des Parameters scopeType wird über eine Implementierung des Interface SessionReferenceProvider verarbeitet. Sie können demnach durchaus ein eigenes Session-Management in Ihrer JCo-basierten Anwendung anbieten. Zur Registrierung Ihrer Implementierung verwenden Sie die Methode registerSessionReferenceProvider der Klasse Environment. Einfach ausgedrückt, werden über den Parameter scopeType Teile der laufenden Java-seitigen Transaktion in kleinere Einheiten unterteilt. Die Nutzung dieser Funktionalität setzt voraus, dass die Infrastruktur eine entsprechende Unterstützung liefert.
Häufig ist es notwendig, dass Aufrufe mehrerer Funktionsbausteine in einer Transaktionsklammer ausgeführt werden. Der SAP Java Connector bietet dafür die Klasse JCoContext an. Zustandsbehaftete Aufrufe sind dadurch gekennzeichnet, dass für die Kommunikation mit dem angegebenen Backend dieselbe Verbindung verwendet wird. Sie müssen jedoch explizit die Kommunikation als zustandsbehaftet markieren. Zu diesem Zweck müssen zum Start der Kommunikation die statische Methode begin der Klasse JCoContext und zum Abschluss der Kommunikation explizit die Methode end aufgerufen werden.

Verbindungstypen für die Kommunikation mit dem Backend

Für den Verbindungsaufbau werden zwei verschiedene Verbindungstypen angeboten. Zum einen ist es möglich, eine sogenannte direkte Verbindung aufzubauen, zum anderen können gepoolte Verbindungen verwendet werden. Gepoolte Verbindungen haben den Vorteil, dass Verbindungsobjekte vor der Nutzung nicht erst erzeugt werden müssen, sondern aus einem Pool bezogen werden können. Der Nachteil von gepoolten Verbindungen ist allerdings, dass alle Verbindungen mit demselben Benutzer aufgebaut werden. In vielen Systemen werden beide Verbindungsarten verwendet.

Bei einem Katalogsystem etwa, bei dem Kunden Artikel betrachten und kaufen möchten, wird das Laden der Artikelstammdaten aus dem ABAPBackend über gepoolte Verbindungen durchgeführt, das Absetzen einer Bestellung (Verbuchen der Bestellung im SAP-System) dagegen über eine direkte Verbindung. Somit ist klar ersichtlich, welcher Kunde wann welche Bestellung gesendet hat. Gepoolte Verbindungen werden analog zu direkten Verbindungen über Destinationsdateien erzeugt, wobei die Parameter zusätzlich zu den herkömmlichen Verbindungsinformationen auch Informationen über den Pool beinhalten. In Listing 4.3 fügen wir der in Listing 4.2 eingeführten Konfigurationsdatei drei Eigenschaften hinzu:

Listing 4.3 Konfigurationsparameter eines Verbindungspools

Die Eigenschaft peak_limit legt fest, wie viele Verbindungen für die Destination gleichzeitig aktiv verwendet werden können. Der zweite Parameter pool_capacity definiert, wie groß der Pool ist, und max_get_client_time gibt an, wie viele Millisekunden der SAP Java Connector warten soll, bis ein Timeout gesendet wird, wenn bereits alle Verbindungen im Pool verwendet werden. Die Nutzung einer gepoolten Destination innerhalb einer Anwendung funktioniert analog zur Programmierung mit direkten Verbindungen.
Factory-Klasse »JCo« Zentrale Klasse Neben der reinen Verarbeitung der Daten aus einem SAP-System sind natürlich auch Verwaltungsaufgaben von immenser Bedeutung. Die zentrale Klasse für die Nutzung dieser Funktionen ist die Klasse JCo. Sie ist als abstrakte Klasse implementiert und stellt lediglich statische Klassenmethoden zur Verfügung.
Über die bereitgestellten Methoden können für die Java-Connector-Infrastruktur eigene Monitoring-Werkzeuge implementiert und in eigene Anwendungen integriert werden. Genau das werden wir für die Beispielanwendung im Folgenden tun. Im Verlauf dieses Kapitels werden wir noch häufiger auf die Verwendung der JCo-Klasse zu sprechen kommen. Fürs Erste sollen Sie eine Möglichkeit implementieren, um die Konfiguration einer Destination abzufragen.

Dazu implementieren Sie eine Managementklasse, die für alle verwendeten Destinationen die entsprechenden Informationen abruft und zur Verfügung stellt. Für die Arbeit mit Destinationen kann die Klasse JCo-DestinationMonitor verwendet werden. Sie erhalten eine Referenz auf eine Instanz dieser Klasse durch die Angabe des Destinationsnamens. Die Klasse selbst bietet alle notwendigen Informationen einer Destination an, die Sie benötigen, um zur Laufzeit den Zustand zu einem Backend abzufragen. Listing 4.4 stellt dar, wie diese Informationen über den Aufruf verschiedener Getter-Methoden abgefragt werden können.

Listing 4.4 Abfrage der aktuellen Poolinformationen einer Destination

Neben den reinen Poolinformationen könnten auch Informationen über die konkreten Verbindungsobjekte innerhalb des Pools interessant sein. Diese Informationen können Sie über die JCoConnectionData-Klasse auslesen. Der SAP Java Connector verwaltet für jede Verbindung zum Backend eine Instanz dieses Typs. Die Instanzen werden über den DestinationMonitor verwaltet und können über diesen erfragt werden. Sie erhalten die Liste der aktuellen Verbindungsinformationsinstanzen über den Aufruf der Methode getConnectionsData. Diese Methode liefert eine Instanz der Klasse java.util.List zurück.

Listing 4.5 stellt das Auslesen dieser Daten dar. Wie Sie sehen, wird mit einem simplen Iterator die Liste wiederholt und jede Instanz einzeln verarbeitet.

Listing 4.5 Auflisten der übergebenen Destinationsinformationen

Ausführung der Klasse »JCoFunction«

Nachdem die Verbindung mit dem Backend erfolgreich hergestellt worden ist, kann mit dem eigentlichen synchronen Aufruf des Funktionsbausteins begonnen werden. Dazu verwenden Sie die beiden in Kapitel 2, »Remote Function Call mit ABAP«, bereits vorgestellten Funktionsbausteine Z_IFP_ORDER_CREATE und Z_IFP_ORDER_GETDETAIL. Der Aufruf eines Funktionsbausteins wird in den folgenden Schritten abgearbeitet:

  1. Erstellen eines Funktionsstellvertreter-Objekts für den aufzurufenden Funktionsbaustein
  2. Füllen der Importparameterliste
  3. Aufruf des Funktionsbausteins und Auswerten der Rückgabewerte

Im ersten Schritt ist es notwendig, einen Stellvertreter (Proxy) für den im ABAP-Stack implementierten remotefähigen Funktionsbaustein zu erzeugen. Da der SAP Java Connector eine rein generische API darstellt und keinerlei Unterstützung mittels eines Codegenerators bietet, definiert die JCo API dafür eine generische Klasse. Die Klasse trägt den Namen JCoFunction. Objekte dieses Typs stellen die ABAP-Funktion auf der Java-Seite dar. Somit müssen die Metadaten des Funktionsbausteins, wie sie im ABAP Dictionary definiert sind, bekannt sein. Dies geschieht über ein Objekt der Klasse JCo-Repository. Wie Listing 4.6 zeigt, dient ein Objekt der Klasse JCoDestination als Factory für Repository-Objekte und diese wiederum als Factory für die Erzeugung von JCoFunction-Objekten.

Listing 4.6 Erzeugen eines Funktionsbaustein-Stellvertreters

Der zweite Schritt besteht darin, die Importparameterliste des Funktionsbausteins zu füllen. Der Baustein Z_IFP_ORDER_CREATE besitzt in seinen Importparametern die Felder IM_ORDERHEADER als Struktur sowie IM_TESTRUN und IM_COMMIT als skalare Parameter. Darüber hinaus definiert der Baustein einen Tabellenparameter mit dem Namen TA_ORDERS. Wie bereits angesprochen, stellt das JCoFunction-Objekt die Java-seitige Repräsentation des Funktionsbausteins dar, sodass auch über dieses Objekt sowohl der Zugriff auf die Import- als auch auf die Tabellenparameter gewährleistet ist. Sie erhalten eine Referenz auf die Importparameterliste durch den Aufruf der Methode getImportParameterList. Der Rückgabewert der Methode ist vom Typ JCoParameterList und vom Basis-Interface JCoRecord abgeleitet.
Die Basisklasse stellt eine Vielzahl von Methoden mit dem Namen setValue für das Füllen der Importparameter bereit. Die Parameter der Methode folgen der üblichen Java-Konvention für das Setzen von Namen-/Wertpaaren, sodass der erste Parameter der Name des Feldes ist und dem zweiten Parameter der typisierte Wert übergeben wird.

Dabei müssen Sie darauf achten, dass die korrekten Datentypen verwendet werden, um mögliche Laufzeitfehler zu vermeiden. Tabelle 4.1 gibt Ihnen eine Übersicht über das Mapping zwischen ABAP- und Java-Datentypen. Wie Sie der Tabelle entnehmen können, wird zum Beispiel ein ABAP-Feld vom Datentyp C als String-Datentyp an die setValue-Methode übergeben.

Mapping der ABAP-Datentypen auf Java-Datentypen
ABAP Kurzbeschreibung Java JCO-Metadaten
B 1-Byte-Integer Int TYPE_INT1
S 2-Byte-Integer Int TYPE_INT2
I 4-Byte-Integer Int TYPE_INT
C Character String TYPE_CHAR
N numerisches Zeichen String TYPE_NUM
P binär codiertes Dezimalzeichen BigDecimal TYPE_BCD
D Datum Date TYPE_DATE
T Zeit Date TYPE_TIME
F Fließkommazahl Double TYPE_FLOAT
X reine Daten Byte[] TYPE_BYTE
G String mit variabler Länge String TYPE_STRING
Y reine Daten Byte[] TYPE_XSTRING

Tabelle 4.1 Mapping der ABAP-Datentypen auf Java-Datentypen

Nachdem die skalaren Parameter gesetzt worden sind, können Strukturen und Tabellen an die Importparameter übergeben werden. Strukturen und Tabellen werden über die im Interface JCoRecord definierten Methoden getStructure bzw. getTable aus der Importparameterliste bzw. Tabellenparameterliste besorgt. Beide Methoden gibt es in zwei Ausprägungen. Einerseits ist es möglich, über den Index das Feld anzusprechen, andererseits kann auch der Feldbezeichner verwendet werden. Für den Funktionsbaustein Z_IFP_ORDER_CREATE bedeutet dies, dass der Methode getStructure der Wert »IM_ORDERHEADER« übergeben wird.

Die in Listing 4.7 dargestellte Methode zeigt, wie die Felder der Struktur mittels der Methode create-OrderHeaderStructure gefüllt werden.

Listing 4.7 Füllen der Struktur »IM_ORDERHEADER«

Im Anschluss kann der Tabellenparameter TA_ORDERPOS gesetzt werden. Für diesen Zweck implementieren Sie eine Hilfsmethode (siehe Listing 4.8). Die Methode erhält als Aufrufparameter eine Liste von Objekten des selbst definierten Typs Orderpos und zusätzlich das ausgelesene Tabellenparameterobjekt. Die Klasse JCoTable bietet für das Hinzufügen einer neuen Zeile die Methode appendRow an. Nachdem eine neue Zeile hinzugefügt worden ist, können die Felder der neuen Zeile mittels setValue mit Werten belegt werden. Der Methode setValue werden dabei zum einen der Feldbezeichner und zum anderen der zu setzende Wert als Argumente übergeben. Das selbst definierte Interface IFieldNames dient als zentraler Platz für die Definition der String-Konstanten für die Feldbezeichner.

Listing 4.8 Befüllen des Tabellenparameters »TA_ORDERPOS«

Nachdem alle Aufrufparameter gesetzt worden sind, kann die Funktion ausgeführt werden. Die Klasse JCoFunction bietet für das Ausführen eines sRFC-Aufrufs die Methode execute an. Diese Methode erhält als Parameter die Destination, auf der die Funktion ausgeführt werden soll. Nachdem die Funktion erfolgreich auf dem ABAP-Stack verarbeitet worden ist, kann über die Methode getExportParameterList auf die Rückgabe des Funktionsbausteins reagiert werden. Für das Auslesen der Werte aus einer Parameterliste bietet das Interface JCoRecord für jeden unterstützten Datentyp zwei Ausprägungen einer Getter-Methode an. Es gibt jeweils eine Methode für das Auslesen mittels des Feldindex sowie den Namen des Feldes als Aufrufparameter. Dadurch ist es beim Iterieren über alle Felder möglich, einfach über den Feldindex zu arbeiten, wobei beim konkreten Zugriff auf ein spezielles Feld der Feldname verwendet werden kann. Listing 4.9 zeigt den Aufruf der Funktion Z_IFP_ORDER_CREATE und die Verarbeitung des skalaren Exportparameters EX_ORDERID.

Listing 4.9 Ausführen der Funktion »Z_IFP_ORDER_CREATE«

4.1.4 Verarbeitung von Tabellen und Strukturen

Nachdem die Erzeugung einer Bestellung erfolgreich durchgeführt worden ist, lesen Sie im nächsten Schritt die erzeugte Bestellung aus dem ABAPSystem aus. Für diesen Zweck wurde der Funktionsbaustein Z_IFP_ORDER_GETDETAIL geschrieben. Sie werden sich dabei weniger um den eigentlichen Aufruf des Funktionsbausteins kümmern als vielmehr um die Verarbeitung
der komplexen Rückgabewerte des Funktionsbausteins. Wie bereits zu Beginn des Kapitels beschrieben, definiert der Funktionsbaustein den Exportparameter EX_ORDERHEAD und den Tabellenparameter TA_ORDERPOS.
Analog zum Setzen komplexer Parameter werden komplexe Parameter über die Parameterliste der Funktion zurückgeliefert und über die entsprechende Getter-Methode zur Verfügung gestellt. Für das Abfragen von Strukturen aus der Exportparameterliste bietet das Interface JCoParameterList die Methode getStructure an. Die Methode kann entweder mit dem Index des Feldes aufgerufen werden oder mit dem Feldnamen. Nach dem Auslesen des Strukturparameters kann die Struktur weiterverarbeitet werden. Die in Listing 4.10 dargestellte Methode getOrderHead erhält als Aufrufargument die zu verarbeitende Struktur. Vergleicht man das Interface JCoStructure mit dem Interface JCoTable, sieht man, dass beide das Interface JCoRecord erweitern, sodass das Auslesen der Werte aus der Struktur den gleichen Gesetzmäßigkeiten unterliegt, wie sie bereits bei der Tabellenverarbeitung besprochen wurden. Mittels Getter-Methoden und der Angabe des Feldes oder der Angabe des Index kann der Wert typisiert aus der Struktur ausgelesen werden.

Listing 4.10 Verarbeiten der Informationen aus der Struktur »EX_ORDERHEAD«

Listing 4.11 stellt die Methode getOrderPos vor. Die Methode zeigt dabei, wie das serverseitige Objektmodell auf das clientseitige Objektmodell gemappt wird. Clientseitig werden Order-Positionen als Instanzen der Klasse Orderpos
gesehen. Die Methode iteriert über die Tabelle, liest über die Getter-Methoden die Werte aus und übergibt sie den entsprechenden Feldern des Java-Modells. Für die Iteration wird eine einfache for-Schleife verwendet. Beachten Sie dabei den Aufruf der Methode setRow. Die Methode bewirkt beim Aufruf, dass der Cursor jeweils auf die nächste Zeile der Tabelle gesetzt
wird.

Listing 4.11 Verarbeiten des Tabellenparameters »TA_ORDERPOS«

Iteratoren Neben der Nutzung einer einfachen for-Schleife ist es auch möglich, die Klasse JCoFieldIterator zu verwenden oder mittels einer foreach-Methode über eine JCoTable-Instanz zu iterieren. Listing 4.12 stellt die Nutzung beider Möglichkeiten gegenüber. Welche der beiden Arten verwendet wird, bleibt Ihnen selbst überlassen. Die einzige Entscheidungshilfe könnte die
Lesbarkeit des Codes sein, denn aus Performancesicht sind die while-Schleife und die foreach-Schleife identisch. Wie Sie sehen, wird beim Zugriff auf ein Feld die Klasse JCoField verwendet. Die Nutzung dieser Klasse im Vergleich zu einem direkten Aufruf der Getter-Methode hat den Vorteil, dass Sie direkt auf die Metadaten und den Wert eines Feldes zugreifen können.

4.1 SAP Java Connector

Listing 4.12 Verwendung der Klasse »JCoFieldIterator«

Die eigentliche Ausführung des Funktionsbausteins erfolgt über die Methode execute auf der JCoFunction-Instanz. Die Methode wird in drei verschiedenen Ausprägungen angeboten. Jede Ausprägung stellt jeweils einen Stellvertreter für die drei Kommunikationsarten zwischen Java und ABAP dar.

4.1.5 Transaktionaler RFC

Der SAP Java Connector bietet neben dem herkömmlichen synchronen Aufruf eines remotefähigen Funktionsbausteins auch die Möglichkeit, einen transaktionalen RFC durchzuführen. Der Unterschied zum synchronen Aufruf eines Funktionsbausteins ist marginal. Er besteht lediglich in der Verwaltung der Transaktions-IDs auf der Seite der externen Java-Anwendung. Zur Erzeugung der Transaktions-ID, die durch das SAP-System zur Verfügung gestellt wird, wird über die Klasse JCoDestination die
Methode createTID angeboten. Das Ergebnis dieses Methodenaufrufs ist die Transaktions-ID als String-Objekt. Da nun die Transaktions-ID und das JCoFunction-Objekt vorliegen, kann die Funktion über ihre execute-Methode aufgerufen werden; dabei werden die Destination sowie die zuvor erzeugte Transaktions-ID übergeben. Listing 4.13 zeigt die main-Methode der Klasse TRFCDemo.

Listing 4.13 Erzeugen einer Liste von Bestellungen über tRFC

Der SAP Java Connector bietet für die Verwaltung von Transaktionsnummern auf der Seite der externen Java-Anwendung keinerlei Möglichkeiten zur Verwaltung der verwendeten Transaktionsnummern an. Sie müssen deshalb selbst dafür sorgen, dass nicht korrekt verarbeitete, transaktionale Funktionsbausteinaufrufe entsprechend wiederholt werden. Verwalten von Transaktionsnummern
In Listing 4.13 wird für die Speicherung von Transaktions-IDs eine Implementierung des Interface TRFCHandlerIF verwendet. Die konkrete Implementierung wird über eine Factory-Klasse realisiert, sodass die Verwendung komplett abstrahiert ist. In den Programmierbeispielen, die Sie zu diesem Buch herunterladen können, finden Sie eine Klasse namens TRFCHashHandler,
die auf der Klasse java.util.Hashtable basiert. Die Hash-Tabelle speichert das JCoFunction-Objekt mittels der Transaktionsnummer
ab. Wie Listing 4.14 zeigt, wird innerhalb der eigentlichen Verarbeitung des Funktionsbausteins eine Instanz des Interface TRFCHandlerIF erzeugt. Durch das Speichern des JCoFunction-Objekts in der Hash-Tabelle ist es im Fehlerfall möglich, die Funktion unter der Angabe der Transaktions-ID nochmals auszuführen.

Listing 4.14 Speicherung der Transaktionsnummern

4.1.6 Queued RFC

Zu guter Letzt betrachten wir in diesem Abschnitt die Nutzung des queued RFC (qRFC). Die Programmierung von qRFCs ist der Entwicklung einer tRFC-basierten Verarbeitung sehr ähnlich. Ein qRFC-Aufruf definiert, wie bereits beschrieben, einen asynchronen Verbuchungsprozess. Dies bedeutet, dass der Aufruf eines Funktionsbausteins als qRFC keine Ergebnisse in
einer Exportparameterliste zurückerhält, selbst wenn diese im Funktionsbaustein in einen Exportparameter geschrieben wurden. Zudem werden die Aufrufe, die an eine Queue gebunden werden, so lange in die Queue eingereiht, bis diese aktiviert wird. Im Anschluss an die Aktivierung kann die Queue geleert werden. Die eingereihten Aufträge werden dann abgearbeitet. Wir zeigen Ihnen die Verwendung des qRFC mit dem SAP Java Connector anhand des bereits bekannten Funktionsbausteins Z_IFP_ORDER_CREATE.
Dabei liegt das Hauptaugenmerk auf dem Unterschied zum tRFC. Das Programmierbeispiel in Listing 4.15 stellt diese Unterschiede im Code dar.

Listing 4.15 qRFC-Verarbeitung des Funktionsbausteins »Z_IFP_ORDER_CREATE«

Nachdem der Funktionsbaustein verarbeitet worden ist, können Sie über Transaktion SMQ2 die gerade erzeugte Queue EAIQUEUE einsehen. Die Transaktion liefert, wie in Abbildung 4.4 gezeigt, eine Übersicht über die aktuellen Queues.


Abbildung 4.4 Übersicht über die im System vorhandenen Queues

Durch einen Doppelklick auf eine Queue können Sie sich deren Inhalt anschauen. Wie Sie Abbildung 4.5 entnehmen können, werden fünf Einträge angezeigt. Es werden also fünf Bestellungen im System erzeugt. Da die Queue noch nicht geschlossen ist, wurden die Funktionsaufrufe noch nicht abgearbeitet. Jeder Eintrag ist einer Transaktions-ID zugeordnet. Mithilfe der (F5)-Taste oder über das Kontextmenü Bearbeiten  Entsperren wird die
Queue entsperrt, und die Einträge in der Queue werden abgearbeitet.


Abbildung 4.5 qRFC-Monitor der Queue »EAIQUEUE«

Bei der Ausführung der Funktionsbausteine haben wir bereits die Bedeutung des Repository-Gedankens angesprochen. Repositorys werden für die Bereitstellung der Schnittstellenbeschreibungen der Funktionsbausteine verwendet. Dabei sind sie auch die zentrale Stelle für das Caching dieser Beschreibungen. Metadaten eines Funktionsbausteins werden nicht direkt nach dem Aufruf des Funktionsbausteins gelöscht, sondern so lange gehalten, wie das Repository-Objekt bestehen bleibt. Jedes Repository-Objekt ist genau einer Destination zugeordnet. Im Folgenden beleuchten wir die Arbeit mit Objekten der Klasse JCoRepository etwas genauer. Verwaltung von Repositorys Für die Arbeit mit den Repository-Objekten erweitern wir die bereits diskutierte
Verwaltungsfunktionalität um Aspekte für die Arbeit mit den verwendeten Repositorys. Mögliche Funktionen, die implementiert werden können, sind zum Beispiel das Auslesen der Funktionsbausteinbeschreibungen im Cache oder die Nutzung der Metadaten eines Funktionsbausteins. Der Zugriff auf die vorhandenen Repositorys kann über mehrere Wege erfolgen. Zum Beispiel kann das Repository über die Destination abgefragt werden, aber auch über die Factory-Klasse JCo. In dem hier diskutierten Bespiel werden die Klasse JCo und die dort implementierte Methode get-Repository verwendet. Die Methode erhält als Parameter die ID des Repositorys, das ausgewertet werden soll. Die ID eines Repositorys ist eine Zeichenkette, die automatisch durch den SAP Java Connector erzeugt wird. Die Adressierung eines konkreten Repositorys ist aus diesem Grund eigentlich unmöglich, sodass getRepository erst dann aufgerufen werden kann, wenn die ID erfragt wurde. So setzt sich die Arbeit mit dem Repository aus
zwei Schritten zusammen: Im ersten Schritt werden die IDs der vorhandenen Repositorys ausgelesen, und im zweiten Schritt wird die Methode getRepository aufgerufen.

4.1.7 Metadatenverarbeitung

Neben der reinen Verarbeitung der Daten mittels eines Funktionsbausteins ist die Metadatenbeschreibung der Schnittstelle des Funktionsbausteins für die tägliche Arbeit sehr interessant. Zum Beispiel können Spaltenbezeichnungen eines Tabellenparameters bei der Darstellung der Daten im Frontend verwendet werden. Metadaten werden über das Interface JCo-MetaData repräsentiert. Zusätzlich bietet die JCo API das direkt abgeleitete Interface JCoListMetaData an. Es kommt immer dann zum Einsatz, wenn die gesamte Schnittstelle benötigt wird, das bedeutet Import-, Export- und Changing-Parameter sowie die Liste der Ausnahmen eines Funktionsbausteins.

Den Zugriff auf die genannten Objekte erhalten Sie über verschiedene Wege. Zum einen können Sie direkt über die Parameterlistenobjekte des Funktionsbausteins und die dort definierte Methode getMetaData auf die Informationen zugreifen oder aber über den Aufruf der Methode get-FunctionTemplate. Diese Methode liefert ein Objekt vom Typ JCoFunction-Template zurück. Die Klasse stellt die komplette Metadatenbeschreibung des gesamten Funktionsbausteins bereit und ist die Vorlage für die Erzeugung des konkreten JCoFunction-Objekts. Bei Betrachtung wird genau eine Instanz vom Typ JCoFunctionTemplate im Repository der Destination gespeichert, solange der Metadaten-Cache nicht invalidiert wird. Dabei stellt das Objekt der JCoFunctionTemplate-Instanz des Funktionsbausteins die Schablone für die Erzeugung des Funktions-Proxys dar. Die Proxy-Klasse definiert für den Zugriff auf die Metadaten die Methoden getImportParameterList, getExportParameterList, getChangingParameter-List, getTableParameterList sowie die Methode getFunctionInterfaceList. Die vier erstgenannten Methoden liefern dabei konkrete Metadaten des zugrundeliegenden Parameterlistentyps. Die Methode getFunctionInterfaceList hingegen liefert alle Metadaten der Parameterlisten insgesamt zurück; eine Unterscheidung, welche Parameterart aktuell ausgelesen wird, wird dabei nicht angegeben.

Listing 4.16 zeigt die Verarbeitung der Metadaten aller Funktionsbausteinmetadaten im Cache.

Listing 4.16 Verarbeitung der Metadaten aller Funktionsbausteine im Cache eines Repositorys

4.2 SAP Enterprise Connector

Bis hierher haben wir die Verwendung des SAP Java Connectors eingehend diskutiert. Sie haben erfahren, dass dies die Technologie ist, mit der Sie eigenständige Java-Anwendungen in Ihre Anwendungslandschaft integrieren können. Dabei ist keinerlei Werkzeugunterstützung nötig, da davon ausgegangen wird, dass Sie keine SAP-Entwicklungswerkzeuge verwenden. Dies kann aber oft mühsam sein, besonders dann, wenn die Funktionsbausteine sehr komplex sind.
Implementieren Sie Anwendungen im Rahmen von SAP NetWeaver, etwa eine Webanwendung, die auf dem SAP NetWeaver Application Server Java ausgeführt wird, ist die Werkzeugunterstützung sehr gut. Für die Programmierung solcher Anwendungen bietet sich das SAP NetWeaver Developer Studio an, das Codegeneratoren umfasst, die Sie beim Konsumieren von Funktionsbausteinen unterstützen. Ein Codegenerator, der im angesprochenen Kontext der Webprogrammierung zum Einsatz kommt, ist der SAP Enterprise Connector (ECo).
Der SAP Enterprise Connector implementiert einen Assistenten, der Sie bei Codegenerierung der Erzeugung der Aufrufklassen für den Funktionsbaustein unterstützt. Der SAP Enterprise Connector erzeugt dabei für alle komplexen Datentypen passende Wrapper-Klassen und zusätzlich Proxy-Klassen für das Konsumieren des Funktionsbausteins. Dadurch wird die Programmierung stark vereinfacht. Grundsätzlich ist der SAP Enterprise Connector dabei keine neue Technologie, da unter der ECo API die JCo API verwendet wird. In SAP NetWeaver Developer Studio 7.0 und 7.3 wird dabei nicht die JCo API in Version 3.x, sondern in Version 2.x verwendet. Wir erzeugen nun mittels des SAP Enterprise Connectors eine Liste der verbuchten Bestellungen. Dazu starten Sie das SAP NetWeaver Developer Studio. Sollten Sie noch keine Installation des Studios besitzen, können Sie sich eine Version vom SAP Service Marketplace herunterladen. Wir gehen bei der Diskussion des SAP Enterprise Connectors davon aus, dass Sie bereits
über umfassende Java-Kenntnisse verfügen, und erklären daher die Erzeugung eines Java-Projekts nicht mehr.

SAP Enterprise Connector bis SAP NetWeaver 7.3
Der SAP Enterprise Connector ist lediglich in Version 7.3 des SAP NetWeaver Developer Studios verfügbar und wird dort als deprecated markiert. Das liegt daran, dass der SAP Enterprise Connector, wie bereits erwähnt, noch auf Version 2.0 des SAP Java Connectors beruht und bis jetzt noch nicht für Version 3.0 angepasst und in das NetWeaver Developer Studio integriert wurde. Nichtsdestoweniger gibt es noch einige Kunden die den Java Enterprise Connector in ihren Projekten verwenden.

4.2.1 Erzeugen von Proxy-Klassen

Nachdem Sie ein Java-Projekt erstellt haben, können Sie mit der Erzeugung der Proxy-Klassen beginnen. Dazu selektieren Sie das entsprechende Projekt und wählen aus dem Kontextmenü New > Other > SAP Connectivity aus. Der sich öffnende Assistent, wie ihn Abbildung 4.6 zeigt, startet mit den Angaben für die zu erzeugenden Proxy-Klassen. In den beiden oberen Feldern besteht die Möglichkeit, die Location für die erzeugten Klassen anzugeben. In diesem Beispiel wählen Sie das Feld Source Folder. Im
Anschluss geben Sie in das Feld Package einen Paketbezeichner ein, der Ihren Programmierrichtlinien entspricht. Das Feld Name erhält den Namen der Proxy-Klasse, die als Kommunikationsfassade für den Funktionsbaustein verwendet wird. Der Klassenname trägt per Konvention die Endung _PortType.


Abbildung 4.6 Angabe der Proxy-Klasse

Proxy-Klasse Nachdem alle Felder ausgefüllt worden sind, bestätigen Sie mit Next und gelangen zum zweiten Bild. Wie in Abbildung 4.7 dargestellt, geben Sie nun die Verbindungsinformationen für die Proxy-Erzeugung an. Die Verbindungsinformationen werden lediglich für die Erzeugung herangezogen. Für die spätere Kommunikation müssen Sie die Parameter separat konfigurieren.
Eine Übernahme aus dem Assistenten ist nicht möglich. Für die Kommunikation mit dem Backend zur Erzeugung der Proxy-Klassen haben Sie zwei Möglichkeiten: Zum einen können Sie mittels Lastverteilung (Load Balancing) und zum anderen per Single-Server Kontakt mit Ihrem SAP-System aufnehmen. Wählen Sie die Ihrer aktuellen Konfiguration entsprechende Registerkarte. In dem hier besprochenen Beispiel verwenden Sie die Registerkarte Single Server und tragen die Kommunikationsparameter ein (siehe Abbildung 4.7). Die Parameter erinnern sehr an die Verbindungsparameter des SAP Java Connectors. Dies ist wenig verwunderlich, da unter
dem ECo-Assistenten die bekannten JCo-Klassen verwendet werden.


Abbildung 4.7 Angabe der SAP-Verbindungsparameter

Verbindungsparameter

Nachdem Sie die Verbindungsparameter eingegeben haben, bestätigen Sie Ihre Eingaben mit dem Button Next. In seltenen Fällen kann es vorkommen, dass Sie bei der Verwendung eines SAP-Routers die Meldung erhalten, dass das Backend-System nicht erreicht werden konnte. Nachdem Sie sich versichert haben, dass Ihre Eingaben korrekt sind, kopieren Sie den SAPRouter-String aus dem Feld, um ihn anschließend wieder in das Feld einzufügen. Klicken Sie wieder auf Next. Sie sollten nun mit dem nächsten Schritt fortfahren können.

Im nächsten Bildschirm können Sie nun über die Suchmaske die Funktionsbausteine suchen, für die Sie ECo-Proxy-Klassen erzeugen möchten (siehe Abbildung 4.8). Wie bereits angesprochen, rufen Sie den Funktionsbaustein Z_IFP_ORDER_GETDETAIL mittels des SAP Enterprise Connectors auf. Geben Sie den Funktionsbausteinnamen in das Feld Function Name ein, und klicken Sie auf den Button Search. Wählen Sie nun den Funktionsbaustein aus, und klicken Sie auf Finish. Der Erzeugungsprozess der Proxy-Klassen wird gestartet, und Sie gelangen nach wenigen Augenblicken in die Java-Perspektive Ihres SAP NetWeaver Developer Studios zurück.


Abbildung 4.8 Auswahl der Funktionsbausteine

Wie Sie feststellen können, erhalten Sie in der Task-Ausgabe der Entwicklungsumgebung eine Vielzahl von Kompilierungsfehlermeldungen. Dies liegt daran, dass das SAP NetWeaver Developer Studio, nachdem die Proxy-Klassen generiert worden sind, nicht in der Lage ist, die richtigen Java-Bibliotheken automatisch in den Klassenpfad des Java-Projekts aufzunehmen. Damit Sie mit der Programmierung fortfahren können, müssen Sie die richtigen JAR-Dateien manuell nachpflegen.
Öffnen Sie für das Hinzufügen der Bibliotheken die Eigenschaften Ihres Projekts, und wechseln Sie dann in den Menüeintrag Java Buildpath. Wechseln Sie nun auf die Registerkarte Libraries, und klicken Sie auf den Button Add Variable. Wählen Sie im darauffolgenden Dialog die Variable ECLIPSE_HOME, und klicken Sie auf Extend. Fügen Sie nun die folgenden JAR-Dateien
hinzu:

  • /com.sap.idb.jcb.core_2.0.0/lib/aii_proxy_rt.jar
  • /com.sap.idb.jcb.core_2.0.0/lib/aii_util_misc.jar
  • /com.mdi_2.0.0/lib/SAPmdi.jar
  • /com.sap.mw.jco_2.0.0/lib/sapjco.jar

Ersetzen Sie dabei durch ECLIPSE_HOME/plugins. Da das Projekt nun keine Fehler mehr enthält, wenden wir uns den erzeugten Klassen zu.
Der SAP Enterprise Connector generiert für jeden strukturartigen Dictionary-Typ, der von einem aufzurufenden RFC-Funktionsbaustein referenziert wird, eine eigene Java-Klasse. Die Proxy-Generierung folgt dabei den Standardkonventionen der JavaBeans-Programmierung. Analog wird bei der Arbeit mit Tabellen verfahren. Tabellenparameter werden wie Listen gehandhabt, dabei erzeugt der SAP Enterprise Connector eine Klasse, die auf den Metadaten der Tabelle basiert. Tabelle 4.2 gibt Ihnen einen kurzen Überblick über die erzeugten Klassen.

4.2 Überblick über die generierten Klassen
Klassenbezeichnung Kurzbezeichnung
 ListOrders_PortType  Proxy-Klasse des Funktionsbausteins
 Z_Ifp_Order_Getdetail_Fault_Exception  Exception-Klasse, die vom Backend im Fehlerfall ausgelöst wird
 Z_Ifp_Order_Getdetail_Fault  Fehlertextklasse
 Z_Ifp_Order_Getdetail_Input  Importparameter
 Z_Ifp_Order_Getdetail_Output  Exportparameter
 ZifporderposType_List  Tabellentyp als Liste

Tabelle 4.2 Überblick über die generierten Klassen

Wie Sie in Tabelle 4.2 sehen, werden neben den ABAP-Dictionary-Klassen zusätzlich je eine Klasse für die Input-Verarbeitung und die Verarbeitung der Import-, Changing- und Tabellenparameter sowie eine Output-Klasse für die Export-, Changing- und Tabellenparameter erzeugt. Für die Bezeichnung der generierten Klassen wird für die Input- bzw. Output-Klasse der Name des Funktionsbausteins (für den hier diskutierten Baustein Z_Ifp_Order_Getdetail) verwendet, gefolgt von der Endung _Input bzw. _Output. Die Fehlerverarbeitung wird selbstverständlich nicht vernachlässigt. Tabelle 4.2 führt zwei Klassen für die Verarbeitung von Exceptions auf. Zum einen gibt es die Klasse Z_Ifp_Order_Getdetail_Fault. Dieser sogenannte Fault-Typ repräsentiert die Ausnahmen des Funktionsbausteins. Mit der Methode getText kann im Java-Client auf den technischen Namen des aufgetretenen Fehlers zugegriffen werden. Instanzen des Fault-Typs werden über die im Funktionsbaustein definierten Exceptions gekapselt. Dazu erzeugt der Codegenerator Klassen mit dem Suffix _Exception.

Wie Sie im noch folgenden Listing 4.17 sehen werden, wird die Exception Z_Ifp_Order_Getdetail_Fault_Exception über einen try-catch-Block verarbeitet. Die Fehlermeldung kann dabei über die Methode getZ_Ifp_Order_Getdetail_Fault ausgelesen werden.

Für den Verbindungsaufbau verwendet der SAP Enterprise Connector zum anderen die Klasse JCo.Client. Instanzen werden dabei über die Factory-Klasse JCo mittels der Methode createClient erzeugt. Die Methode wird in verschiedenen Ausprägungen angeboten. Da wir die Konfigurationsparameter nicht direkt im Code ablegen möchten, übergeben wir ein Objekt der Klasse java.util.Properties. Die Instanz wird, analog zur Verwendung beim Java Connector 3.0, über eine Property-Datei konfiguriert. Nach erfolgreicher
Konfiguration kann ein Verbindungsaufbau mit dem Backend durchgeführt werden. Dies geschieht über die Methode connect.

4.2.2 Programmierung des Clients

Im nächsten Schritt kann der eigentliche Aufruf des Funktionsbausteins vorbereitet werden. Dazu instanziieren wir die Proxy-Klasse ListOrders_PortType und setzen zuallererst den Kommunikations-Client über die Methode setJCoClient. Wie Sie feststellen können, bietet der SAP Enterprise Connector die Möglichkeit, über verschiedene Setter-Methoden die Kommunikation zwischen dem Java-Client und dem ABAP-Backend zu konfigurieren. Im dargestellten Beispiel möchten wir lediglich einen einfachen
sRFC-Aufruf ohne weitere Kommunikationsaspekte nutzen, sodass wir es bei der Übergabe der JCo.Client-Instanz belassen.
Im Folgenden setzen wir die Importparameter des Funktionsbausteins. Der Codegenerator hat typisierte Klassen für alle notwendigen Parametertypen des Backends erzeugt. Für den Importparameter verwenden wir die Klasse Z_Bapi_Order_Getdetail_Input. Nach der Instanziierung können wir den Parameter orderid über die Methode setIm_Orderid eintragen und an die Methode z_Bapi_Order_Getdetail übergeben. Nachdem der Funktionsbaustein ohne Fehler aufgerufen worden ist, kann über die Methode getEx_
Orderhead der Zugriff auf die Exportparameter erfolgen. Wie Sie Listing 4.17 entnehmen können, verwenden wir die bereits in Abschnitt 4.1.3, »Programmierung mit dem SAP Java Connector«, diskutierten Modellklassen zur Verwaltung der Bestellinformationen. Die einzelnen Felder des Bestellkopfes werden über die erzeugten Getter-Methoden aus der zurückgelieferten
Exportstruktur ausgelesen.

Zum Schluss rufen wir für die Verarbeitung der Bestellpositionen die Methode getTa_Orderpos auf und verarbeiten die Daten gemäß unserer Modelldefinition. Zum Abschluss kann die Verbindung zum Backend über die Methode disconnect beendet werden.

Listing 4.17 Aufruf des Funktionsbausteins »Z_BAPI_ORDER_GETDETAIL«

4.3 Nutzung generischer Backend-Modelle

Die in Abschnitt 4.2, »SAP Enterprise Connector«, besprochenen Möglichkeiten, um auf das SAP-Backend von einem Java-Client aus zuzugreifen, haben den Nachteil, dass das Verbindungsmanagement durch den Entwickler selbst verwaltet werden muss. Diese Verwaltungsaspekte beginnen bei der Organisation der Konfigurationsparameter für unterschiedliche Backend-Systeme und enden beim reinen Verbindungsmanagement zur Laufzeit. Im Rahmen der Weiterentwicklung von SAP Composition Environment schuf SAP die Möglichkeit, das Verbindungsmanagement der Web-Dynpro-Infrastruktur zu verwenden. Grundlage dafür ist das Bereitstellen eines
sogenannten generischen Backend-Modells. In diesem Abschnitt erläutern wir die Konfiguration und die Nutzung der Bibliothek. Die Nutzung dieser generischen Backend-Modelle wurde auch in den neuen Versionen 7.3 und 7.4 des SAP NetWeaver AS beibehalten.

4.3.1 Generische Proxy-Klassen

Wie Sie sehen konnten, bieten der Java-Connector und seine Abstraktion, der SAP Enterprise Connector, alles, was für die erfolgreiche Implementierung einer SAP-basierten Java-Anwendung benötigt wird. Allerdings ist bei beiden hinsichtlich der Unterstützung beim Verbindungsmanagement, also beim Aufbau und der Verwaltung von Verbindungen zum SAP-System, einiges an Programmieraufwand erforderlich. Im Rahmen von SAP Composition Environment wurde eine neue Möglichkeit für die Implementierung
von Java-Anwendungen eingeführt, nämlich die Nutzung des generischen Backend-Modells.

Das generische Backend-Modell kommt bereits seit dem Start von SAP Net-Weaver 2004 zum Einsatz, und zwar bei Web Dynpro Java. Wie bereits angesprochen, bietet Web Dynpro die Möglichkeit, über den adaptiven RFC Backend-Funktionen zu nutzen. Diese API war jedoch für Nicht-Web-Dynpro-Anwendungen nicht verwendbar. Im Rahmen der Reimplementierung der adaptiven RFC API (ARFC2) wurde mit Release 7.2 von SAP Composition Environment die ARFC2 API auch für den Gebrauch außerhalb von Web Dynpro veröffentlicht.
Web Dynpro bietet für die Kommunikation mit dem Backend, analog zum SAP Enterprise Connector, einen Codegenerator an, mit dem entsprechend der Schnittstellendefinition des RFC Java-Klassen generiert werden. Für die Datentypen aus der Schnittstelle wird ein sogenanntes Modell generiert; ein Modell ist dabei nichts anderes als ein Repository von Java-Klassen.

Betrachtet man die generierten Klassen etwas genauer, kann man feststellen, dass diese Klassen von SAP-spezifischen Java-Klassen abgeleitet sind. Genau diese Java-Klassen können nun eben auch in Nicht-Web-Dynpro-Anwendungen genutzt werden. Man spricht bei diesen Klassen von generischen Klassen. Generisch deshalb, da sie nicht spezifisch für einen bestimmten Funktionsbaustein erzeugt wurden, sondern die Basis für alle Funktionsbaustein-Proxys sind. Durch die Nutzung der ARFC2-Bibliothek ist es möglich, auch in Nicht-Web-Dynpro-Anwendungen das Verbindungsmanagement dieser Technologie zu nutzen. Der Nachteil ist, dass man zum Beispiel in einem JSP-Projekt (Java Server Pages) Abhängigkeiten zu Web-Dynpro-Bibliotheken einführt.

Das generische Modell bezieht die Verbindungsinformationen für ein SAPSystem aus sogenannten JCo-Destinationen. Destinationen besitzen einen eindeutigen Namen, über den sie angesprochen werden können. Dabei sind zwei verschiedene Arten von Destinationen zu unterscheiden:
-* Zum einen gibt es die Modelldatendestinationen, die verwendet werden, um die Verbindungsinformationen für den Aufruf des Funktionsbausteins zu beziehen.
-* Zum anderen gibt es die Metadatendestinationen, die dazu dienen, festzustellen, ob seit der letzten Proxy-Generierung Änderungen am Funktionsbaustein vorgenommen wurden.

Anhand der Metadatenstruktur kann dann entschieden werden, ob die Änderungen zu einem Abbruch der Verarbeitung führen müssen oder ob die Änderungen sich noch in einem Rahmen befinden, der ein erneutes Generieren unnötig macht. Abbrüche können zum Beispiel notwendig werden, wenn Erweiterungen des Funktionsbausteins durchgeführt wurden, ohne die APPEND-Struktur zu verwenden.

Jedes ARFC-Modell und damit auch das hier besprochene darunterliegende generische Modell muss dabei jeweils eine Konfiguration für jeden Destinationstyp besitzen. Die konkreten Verbindungsdaten – etwa welches SAPBackend oder welche Art der Authentifizierung verwendet wird – können sich unterscheiden. Gerade bei der Authentifizierung sind zwei verschiedene
Destinationen sinnvoll. Für die Metadatendestination ist es ausreichend, dass lediglich ein technischer Benutzer statisch bei der Destination eingetragen wird, da an dieser Stelle keine Änderungen am Datenbestand des Backend-Systems zu erwarten und dadurch auch einfache Berechtigungen ausreichend sind. Dem gegenüber steht die Modelldatendestination; hier sollte dringend auf einen statischen Benutzer verzichtet und die Authentifizierung über das SAP-Logon-Ticket durchgeführt werden. Der
Grund für die Unterscheidung ist klar: Bei der Modelldatendestination werden Funktionsbausteine aufgerufen, und diese können Änderungen am Datenbestand vornehmen. Dadurch muss eine tatsächliche Authentifizierung des Aufrufers durchgeführt werden.

4.3.2 Klassenabhängigkeiten

Generische Klassen Bevor wir nun mit der eigentlichen Implementierung beginnen, betrachten wir die Klassen des generischen Modells. Wir beschränken uns dabei auf die Klassen, die wir für den erfolgreichen Aufruf eines Funktionsbausteins benötigen. Die Liste an Klassen ist dabei übersichtlich; es werden lediglich vier Klassen aus der ARFC API benötigt. Konkret brauchen Sie für den Aufruf eines Funktionsbausteins mit der ARFC-Bibliothek folgende Klassen:

  1. ARFC2ModelInfo
  2. ARFC2Model
  3. ARFC2GenericModelClass
  4. ARFC2GenericModelClassExecutable

Das Sequenzdiagramm in Abbildung 4.9 zeigt die Abarbeitungsschritte aus Sicht einer Java-Klasse (im Bild als Aufrufer bezeichnet). Wie im Sequenzdiagramm zu sehen ist, steht im Mittelpunkt des generischen Ansatzes die Klasse ARFC2Model.


Abbildung 4.9 Aufrufsequenz eines generischen Modells

Die Klasse dient als Bindeglied zwischen dem ausführbaren generischen Funktionsbaustein-Proxy und den Metadaten, mit denen der Proxy gefüttert wird. Die Metadaten eines Funktionsbausteins werden über die Klasse ARFC2ModelInfo beschrieben. Objekte dieses Typs werden über den Aufruf der statischen Methode importMetadataFromBackend erzeugt und initialisiert.
Die Methode erhält als Aufrufparameter unter anderem den Bezeichner der Metadatendestination. Diese Metadatendestination muss
entsprechend auf dem Applikationsserver konfiguriert sein. Aufruf des Funktionsbausteins
Der Aufruf des Funktionsbausteins wird über die Klasse ARFC2-Generic-ModelClassExecutable durchgeführt. Die Klasse bietet dazu die Methode execute an. Vor der Ausführung müssen jedoch noch die jeweiligen Felder gefüllt werden, die an den Funktionsbaustein übergeben werden sollen. Dazu wird die Methode setAttributeValue angeboten. Die Methode erhält zwei Parameter: zum einen den Namen des Feldes und zum anderen den Wert als komplexe Datenstruktur. Komplexe Datenstrukturen werden beim generischen Ansatz mittels der Klasse ARFC2GenericModelClass beschrieben. Objekte dieses Typs können auf dem Objekt ARFC2GenericModelClassExecutable mittels der Methode getRelatedModelObject ausgelesen werden.

4.3.3 Konfiguration der Destinationen

    Bevor mit der eigentlichen Programmierung begonnen werden kann, müssen Sie die Infrastruktur entsprechend aufsetzen. Dazu zählt aus Sicht eines Entwicklers die Konfiguration der JCo-Destinationen. Zur Konfiguration der Destinationen muss der SAP NetWeaver Application Server gestartet sein.

  • Öffnen Sie zur Konfiguration der JCo-Destinationen ein Browserfenster, und starten Sie den SAP NetWeaver Administrator über die URL http://servername:port/nwa. Dabei ersetzen Sie servername und port durch die jeweiligen Werte Ihrer Installation. Wählen Sie aus dem Navigationsmenü den Eintrag Configuration aus, und navigieren Sie im Anschluss über den Eintrag Infrastructure zur Übersicht der Konfigurationsoptionen für diesen Bereich. Klicken Sie nun auf den Link Destinations zur Konfiguration von Verbindungen.
  • Wie bereits eingangs erwähnt, benötigen wir zwei Verbindungen zu unserem Backend-System – eine Verbindung für die Metadaten und eine Verbindung für die tatsächlichen Anwendungsdaten. Wir erzeugen im ersten Schritt die Destination für das Auslesen der Metadaten. Klicken Sie dazu auf die Schalfläche Create. Geben Sie im Anschluss den Bezeichner Ihrer Destination in das Feld Destination Name ein; wir wählen für das hier vorgestellte Beispiel den Namen »EAI_RFC_METADATA_DEST« und für das Feld Destination Type den Wert »RFC« aus. Klicken Sie im Anschluss auf den Button Next. Abbildung 4.10 zeigt diesen Konfigurationsschritt.


    Abbildung 4.10 Konfiguration des Destinationsbezeichners und Typs

  • Sie befinden sich nun im Schritt Connection and Transport Security Settings. Geben Sie hier, je nach Systemlandschaft, die Verbindungsinformationen zu Ihrem Backend-System ein. Bestätigen Sie nach erfolgter Eingabe der Parameter mit dem Button Next.
  • Der nächste Konfigurationsschritt enthält die Logon-Daten zur Authentifizierung beim Zugriff auf die Metadaten des gewünschten Funktionsbausteins. Füllen Sie die Felder gemäß Ihrer Infrastruktur aus (siehe Abbildung 4.11). Schließen Sie die Konfiguration über Finish ab.


    Abbildung 4.11 Konfiguration der Authentifizierungsinformationen

  • Nachdem die erste JCo-Destination erzeugt worden ist, kann mit der zweiten Destination das Besorgen der Anwendungsdaten erfolgen. Wir wählen dazu den Destinationsbezeichner EAI_MODELDATA_DEST. Die Konfiguration unterscheidet sich in den ersten Schritten wenig von der Konfiguration der Metadaten. Eine Ausnahme besteht allerdings im Konfigurationsschritt Logon Data. Nachdem Sie die Authentifizierungsinformationen eingegeben haben, wählen Sie im Bereich Repository Connection den Namen der Metadatendestination (EAI_RFC_METADATA_DEST) aus. Dazu nutzen Sie den Button zur Suchhilfe und selektieren die Destination (siehe Abbildung 4.12). Bestätigen Sie Ihre Eingaben durch einen Klick auf die Schalfläche Finish.


    Abbildung 4.12 Verknüpfung der Metadatendestination mit der Anwendungsdatendestination

    Nach dem Bestätigen des letzten Schrittes gelangen wir wieder in den Ausgangsbildschirm. Wir haben nun zwei neue Destinationen vom Typ RFC-Destination erzeugt. Abbildung 4.13 zeigt die konfigurierten JCo-Destinationen. Zum Abschluss der Konfiguration können Sie durch Auswahl einer Destination und mit dem Button Ping Destination prüfen, ob die Verbindung korrekt konfiguriert wurde.


    Abbildung 4.13 Übersicht über die konfigurierten JCo-Destinationen

    4.3.4 Implementierung

    Nach der Konfiguration der Destinationen können Sie mit der Implementierung des Java-Clients beginnen. Die Basis dieses Clients ist ein Servlet. In diesem Servlet wird der Funktionsbaustein Z_IFP_ORDER_GETDETAIL aufgerufen und innerhalb des Browsers angezeigt.

Zur Erstellung eines Servlets ist es notwendig, ein sogenanntes Dynamic Web Project zu erstellen. Dieses Projekt wird als Container für Servlets und JSP-Seiten verwendet, also für dynamische Webanwendungen.

  1. Starten Sie dazu das SAP NetWeaver Developer Studio, und navigieren Sie zum Menüpunkt File  New Project. Wählen Sie im folgenden Dialog den Menüpunkt Web > Web Project.
  2. Abbildung 4.14 zeigt diesen Dialog. Füllen Sie das Feld Project name aus, und wählen Sie die Checkbox Add project to an EAR. Das Häkchen bewirkt, dass innerhalb des Assistenten auch ein Projekt für das Deployment des Web Projects erzeugt wird. Vergleichen Sie dazu Ihren Bildschirm mit Abbildung 4.14.
  3. Nach der Bestätigung mit Next prüfen Sie im folgenden Bildschirm, ob das Häkchen bei Generate Deployment Descriptor gesetzt ist, und beenden den Dialog mit Finish.
  4. Nachdem das Projekt erfolgreich angelegt worden ist, wählen Sie im dem Projektbezeichner das Kontextmenü aus. Innerhalb des Menüs wählen Sie Other. Im darauffolgenden Dialog wählen Sie den Menüeintrag Web Project > Servlet aus. Es erscheint der in Abbildung 4.15 dargestellte Dialog.


    Abbildung 4.14 Erstellen eines Web Projects


    Abbildung 4.15 Hinzufügen eines Servlets

  5. Füllen Sie in diesem Bildschirm die Felder Java package und Class name aus, und übernehmen Sie, wenn Sie möchten, die Beispieldaten aus Abbildung 4.15. Bestätigen Sie nach dem Ausfüllen der beiden Felder mit
    Next. Sie erhalten den in Abbildung 4.16 gezeigten Dialog.


    Abbildung 4.16 Definition des URL-Mappings

  6. Der Dialog gibt Ihnen die Möglichkeit, ein sogenanntes URL-Mapping durchzuführen. Dieses Mapping dient dazu, den Aufruf des Servlets über einen einfacheren Namen als den eigentlichen Servlet-Namen zu ermöglichen. Füllen Sie das Feld Description aus. Klicken Sie nun auf Next.
  7. Zum Abschluss gelangen Sie in ein Bildschirmbild, in dem festgelegt werden kann, welche Methoden beim Anlegen automatisch erzeugt werden sollen. Behalten Sie die Standardeinstellungen bei. Dadurch werden die Methoden doGet und doPost erzeugt. Die beiden Methoden werden bei der Servlet-Verarbeitung entsprechend der Art des Aufrufs (POST oder GET) aktiviert. In unserem Fall sind die beiden Methoden somit für den Aufruf des Funktionsbausteins verantwortlich.

Nach der Generierung der Projekte wechseln Sie zum Webprojekt und öffnen über den Kontextmenüeintrag Properties die Eigenschaften des Projekts.

  • Navigieren Sie zum Eintrag Java Build Path, und klicken Sie dort auf den Registerkarteneintrag Libraries.
  • Klicken Sie auf den Button Add External JAR, und fügen Sie die folgenden Bibliotheken hinzu:
    //J0x/j2ee/cluster/bin/ext/
    tc~cm~arfc2/lib/cm_arfc2_api.jar
    //J0x/j2ee/cluster/bin/ext/
    com.sap.tc.cmi/lib com.sap.tc.cmi_api.jar
    //J0x/j2ee/cluster/bin/ext/
    tc~cm~base/lib cmi_baseimpl_api.jar

  • Klicken Sie nun den Button Add Variable an, und wählen Sie im darauffolgenden Dialog den Eintrag ECLIPSE_HOME aus.
  • Klicken Sie auf Extend. Expandieren Sie im Anschluss den Eintrag plugins, und selektieren Sie dort den Eintrag, beginnend mit com.sap.mw.jco3/lib. Wählen Sie dort die JAR-Datei sapjco3_IDE.jar aus, und bestätigen Sie mit OK.
  • Klicken Sie nun erneut auf Extend. Expandieren Sie wieder den Eintrag plugins, und selektieren Sie dort den Eintrag, beginnend mit com.sap.exception/lib. Wählen Sie dort die JAR-Datei sap.com~tc~exception~impl.jar aus, und bestätigen Sie mit OK.

Da nun die Konfiguration des Projekts abgeschlossen ist, können Sie mit der Implementierung beginnen. Zu Beginn legen Sie eine Klasse mit dem Namen EaiConst an. Innerhalb dieser Klasse definieren Sie einige Konstanten, die Sie im Lauf der Implementierung verwenden werden. Listing 4.18 zeigt diese Konstanten.

Listing 4.18 Konstantendefinition

Basierend auf den Erkenntnissen aus dem Sequenzdiagramm, fügen Sie eine private Methode ein, mit der eine Instanz der Klasse ARFC2ModelInfo angelegt werden kann. Sie rufen dabei die statische Factory-Methode importMetadataFromBackend auf. Die Methode enthält sieben Parameter, wobei Sie nur fünf für unsere Zwecke füllen müssen. Der erste Parameter dient dabei der Spezifizierung, welche Art von Daten erzeugt werden soll. Sie übergeben, da es sich um Metadaten handelt, den String Metadata. Diese Angabe ist auf den ersten Blick etwas unverständlich, denn die Methode sollte doch wissen, welche Information gezogen werden soll. Man darf an dieser Stelle jedoch nicht vergessen, dass es sich bei dieser API um eine Bibliothek handelt, die nicht originär für den direkten Aufruf implementiert wurde, sondern eigentlich unter Web Dynpro ihre Arbeit erledigt. Daher ist die Angabe einiger Aufrufparameter möglicherweise schwer zu verstehen. Als zweiten Parameter übergeben Sie die Sprache, um sprachabhängige Metainformationen zu laden. Der dritte Parameter beschreibt den Namen des Funktionsbausteins und der vierte den Namen der Destination. Dabei ist wichtig, dass die Destination als Metadatendestination angelegt ist. Mithilfe von HasMap als fünftem Parameter können weitere Konfigurationsinformationen übergeben werden; dies ist allerdings bei Nutzung, zum
Beispiel aus einem Servlet heraus, nicht notwendig.

Die Methode createModelInfoObject wird ihrerseits aus einer weiteren privaten Methode aufgerufen. Sie trägt den Namen executeAndShowOutput und wird in Listing 4.19 gezeigt. Sie wird aus doGet bzw. doPost des Servlets aufgerufen und erhält Objekte für den Zugriff auf den Servlet-Request bzw. die Servlet-Response. Wir konzentrieren uns an dieser Stelle lediglich auf die
ARFC-modellspezifischen Zeilen und sehen, dass nach dem Aufruf von createModelInfoObject eine neue Instanz der Klasse ARFC2Model angelegt wird. Der Konstruktor der Klasse erhält dabei zwei Parameter: Der erste Parameter ist die eben erzeugte Metadateninstanz vom Typ ARFC2ModelInfo; der zweite Parameter ist vom Typ String und enthält den Bezeichner der Application-Data-Destination.

Listing 4.19 Implementierung der Methode »createModelInfoObject«

Nun können die Vorbereitungen für den Aufruf des Funktionsbausteins abgeschlossen werden; der letzte Schritt ist das Füllen von Import- oder Tabellenparametern. Analog zur Nutzung des SAP Enterprise Connectors wird dieses Befüllen auf dem Proxy des Funktionsbausteins durchgeführt. Dies ist unter ARFC eine Instanz vom Typ ARFC2GenericModelClassExecutable.
Sie erhalten eine solche Instanz durch den Aufruf der Methode create-ModelObject. Das Interessante an dieser Methode ist der Aufrufparameter. Sie übergeben nicht nur den Namen des Funktionsbausteins, sondern hängen an diesen Funktionsbausteinbezeichner noch den Wert »_Input«. Dadurch entsteht die Zeichenkette Z_IFP_ORDER_GETDETAIL_Input. Erinnern Sie sich an die Programmierung
mit dem SAP Enterprise Connector (siehe Abschnitt 4.2): So wurde durch den Codegenerator die Klasse für die Importparameterstruktur analog bezeichnet. Das Konzept ist nicht wirklich neu, sondern befindet sich lediglich auf
einem anderen Abstraktionsniveau. Aus diesem Grund wird es Sie auch nicht wundern, dass Sie nun über das gleiche Konzept die Importparameter füllen sollen, und zwar über den Aufruf der Methode setAttributeValue. Da es sich bei dieser Methode wiederum um eine generische Methode handelt, erhält sie neben dem Wert auch noch den Feldbezeichner. Sie übergeben daher die Werte »Im_Orderid« und »Orderid 43«. Listing 4.20 zeigt die Erzeugung und das Befüllen des Objekts ARFC2GenericModelClass-Executable.

Listing 4.20 Methode »executeAndShowOutput«

Listing 4.21 zeigt im Weiteren die Ausführung und die Darstellung des Ergebnisses durch die private-Hilfsmethode executeRFC. Zu Beginn wird die Methode execute auf der übergebenen Instanz vom Typ ARFC2Generic-ModelClassExecutable ausgeführt. Um nun an die Details der Bestellung zu gelangen, rufen Sie über die Methode getRelatedModelObject und die Übergabe des String-Literals Output die Rückgabedaten des Funktionsbausteins ab. Komplexe Rückgabewerte werden über die Klasse ARFC2Generic-ModelClass geschrieben. Sie iterieren nun im Folgenden über die Output-Informationen und lesen über die Methode getRelatedModelObjects und die Übergabe des Strukturbezeichners die entsprechenden Werte aus.

Listing 4.21 Aufruf des Funktionsbausteins und Ausgabe des Ergebnisses Konfiguration der Laufzeitabhängigkeiten

Bevor Sie nun die Anwendung starten können, müssen Sie sich noch Gedanken über mögliche Abhängigkeiten zwischen der Servlet-Anwendung und anderen Komponenten des Applikationsservers machen. Erinnern Sie sich an den Beginn der Implementierung; dort haben Sie verschiedene Bibliotheken zum CLASSPATH des Developer-Studio-Projekts hinzugefügt – unter anderem auch die ARFC2-Bibliothek. Damit nun die Laufzeitumgebung die Klassen dieser Bibliothek auflösen kann, müssen Sie eine Laufzeitabhängigkeit
zwischen diesem Projekt und der ARFC2-Bibliothek definieren. Laufzeitabhängigkeiten werden bei einem JEE-Projekt im Deployment-
Container der entsprechenden Anwendung definiert. In diesem Beispiel ist dies das EAR-Projekt, das Sie automatisch haben erzeugen lassen. Es trägt den Namen »ARFCServletEAR«, und Sie können es im Projekt-Explorer im SAP NetWeaver Developer Studio sehen. Expandieren Sie dazu den Projektknoten, und navigieren Sie anschließend zum Unterverzeichnis EarContent\META-INF. In diesem Verzeichnis befindet sich eine Datei mit dem Namen j2ee-engine.xml. Mittels dieser Datei kann dem Applikationsserver mitgeteilt werden, wie sich die Anwendung zur Laufzeit verhalten soll bzw. welche zusätzlichen Laufzeitinformationen die Anwendung benötigt. Nachdem Sie die Datei in der XML-Perspektive des SAP NetWeaver Developer Studios geöffnet haben, fügen Sie die Zeilen aus Listing 4.22 zwischen dem XML-Tag application-j2ee-engine ein. Wie Sie dem Listing entnehmen können, wird eine Referenz zwischen der Anwendung und der Bibliothek tc~cm~arfc2 definiert. Es handelt sich dabei um eine sogenannte weak-Reference.

Listing 4.22 Abschließende Konfiguration einer weak-Reference auf die ARFC2-Bibliothek

Change date on Vanilla Forum

To launch files from Chrome’s context menu, I’ve registered a simple new URI handler: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85) i.e. vanied:

Add to Windows registry:

Context Menu Editor plugin for Chrome: cme3.4.0.cab
After installing right click on Options:

Add new item

Edit and apply. Except info.linkUrl there are number of different data: https://developer.chrome.com/extensions/contextMenus#property-onclick-info

Teil 2. Anbindung von Java und SAP

https://pdfslide.net/documents/sap-fuer-java-entwickler-konzepte-schnittstellen-technologien.html

Teil 2. Anbindung von Java und SAP

5. Anbindung von Java und SAP – Übersicht
6. RFC-Schnittstellen und Java Connector
7. IDocs

5 Anbindung von Java und SAP

Übersicht Die Sprache Java hat Einzug in viele Unternehmen gehalten und sich dabei den Ruf als Integrationsplattform erworben. Zu annähernd jeder Technologie und jedem Protokoll stellt Java eine Schnittstelle bereit. Sei es LDAP, IMS oder MQSeries, Java liefert die Funktionalität in abstrahierter Form als APIs wie in diesem Fall JNDI, JCA und JMS.

Daher steht an Knotenpunkten zwischen heterogenen Systemen oft ein Java-Server. Muss man die Daten aus Datenbank X mit denen aus Altsystem Y kombinieren und schließlich in Datenbank Z schreiben, so strickt man sich ein paar passende EJBs und kann darauf aufbauend dem Benutzer eine homogene Oberfläche liefern. Es liegt nahe, dass eines der proprietären Systeme, die es zu integrieren gilt, durchaus auch ein SAP-System sein kann. In großen Unternehmen ist es sogar recht wahrscheinlich, dass ein beliebiger fachlicher Aspekt wenigstens teilweise eine Anbindung an Daten erfordert, die in SAP verwaltet werden. Entsprechend groß ist der Bedarf für eine Kopplung von Java und SAP. In diesem Teil 2 stellen wir zwei unterschiedliche Vorgehensweisen vor, um Java an SAP anzubinden.

Zum einen ist das die Kombination aus RFC und JCo, die primär für synchrone Anbindungen genutzt wird. Sie wird in Kapitel 6 behandelt. Zum anderen ist das die IDoc-Technologie, die asynchrone Anbindungen möglich macht. Sie wird in Kapitel 7 näher beleuchtet. Voraussetzung ist in beiden Fällen ein SAP-System, wie Sie es heute, im Jahr 2005 in der Praxis vorfinden. Also ein R/3-System und kein Netweaver-System, wie es in Teil 3 beschrieben wird. Im Netweaver verschwimmen zwar die Grenzen zwischen Java-Programm und SAP-System und die Koppelung unterschiedlicher Systeme gewinnt einiges an Eleganz. Doch das nützt Ihnen im Alltag wenig, da diese Technologie noch keine nennenswerte Verbreitung gefunden hat. Es lohnt sich also, einen Blick auf die etablierten Anbindungstechniken in diesem Teil des Buches zu werfen.

6 RFC-Schnittstellen und Java Connector

Möchte man ein SAP-System synchron mit einem Fremdsystem koppeln, dann ist die RFC-Technologie die bewährte Lösung. RFC steht für Remote Function Call und bezeichnet einen Mechanismus, um auf einem entfernten System eine Funktion auszuführen. Dabei handelt es sich in der Regel um ein SAP-System. Die Technik ist vergleichbar mit dem RPC-Mechanismus unter Unix.
Ursprünglich wurde RFC über eine C-Bibliothek realisiert, die das Fremdsystem einbinden musste, um mit einem Funktionsbaustein im SAP System zu kommunizieren. Da C relativ mühsam zu programmieren ist, hat sich schnell eine Reihe von Produkten etabliert, die die eigentliche RFC/C-Schnittstelle vor den Benutzern verbergen und handlichere und höherwertige Schnittstellen zur Verfügung stellen. Wenn man heute von einer RFC-Schnittstelle spricht, dann meint man also meist einen Mechanismus, der nur intern das RFC-Protokoll verwendet.
Wir werden hier den RFC-Mechanismus nur über SAPs eigene Implementierung eines Java-Wrappers einsetzen. Der sogenannte Java Connector (JCo) ist das einfachste Mittel, um von Java aus auf ein SAP-System zuzugreifen. Intern verwendet er die ursprüngliche C-basierte RFC-Bibliothek. Sie sollten im Hinterkopf behalten, dass der JCo nur eine von mehreren Möglichkeiten darstellt, die RFC-Schnittstelle zu nutzen. Die übrigen werden in der Regel von kleinen Fremdanbietern hergestellt.
Wie auch immer die Implementierung auf Seiten des Fremdsystems geartet sein mag, die R/3-Seite einer RFC-Verbindung folgt immer derselben Logik. Vereinfachend haben wir gesagt, es handle sich dabei um einen Funktionsbaustein. Um die Datenkonsistenz im SAP-System sicherzustellen, muss man jedoch ein paar Umwege in Kauf nehmen.
Zuerst sollen diese übergeordneten R/3-seitigen Aspekte des RFC Zugriffs beschrieben werden. Anschließend werden die dazu notwendigen Zugriffstechniken auf Java-Seite detailliert untersucht. Im Anschluss folgt ein Abschnitt über Debugging-Techniken und einer über weiterführende Themen.

6.1 RFC auf R/3-Seite
6.1.1 Überblick

In der Regel spielt die SAP-Seite bei einer RFC-Schnittstelle die Rolle des Servers. Dahinter steht der Sachverhalt, dass das SAP-System oft der zentrale Ablageort für unternehmensweit genutzte Daten ist. Die Datenseite ist meist die passive Seite in einem Kommunikationsvorgang, während die andere aktive Seite auf die Daten zugreift. Daher gehen wir davon aus, dass die SAP-Seite passiv ist und somit den Part des Servers übernimmt.

Sollte der Zugriff lediglich zum Auslesen von Daten aus SAP dienen, ist das Vorgehen einfach. Sie implementieren einen Funktionsbaustein, der direkt auf die Tabellen zugreift. Bei dessen Erstellung müssen Sie lediglich darauf achten, dass Sie ihn als remote-fähig markieren. Außerdem sind bei der Auswahl der Parameter einige Regeln zu beachten, die weiter unten diskutiert werden.
Wesentlich schwieriger sind schreibende Zugriffe auf Daten im SAP System zu implementieren. Wenn Sie die vom System vorgegebenen Transaktionen manuell bedienen, sorgen ja viele tausend Zeilen ABAP Code dafür, dass die betriebswirtschaftlichen Tabelleninhalte auf konsistente Art und Weise modifiziert werden. Beispielsweise wird kein Konto belastet, ohne ein anderes zu entlasten. Rein technisch gesehen haben Sie die Möglichkeit, die Tabelleninhalte beliebig zu modifizieren und Kontostände im System nach Belieben zu manipulieren. Doch dadurch verlieren Sie nicht nur die Datenkonsistenz, sondern auch den Haftungsanspruch der Firma SAP für Ihr Produkt. Außerdem müssen Sie Ihren Code nach einem Wechsel des SAP-Releases eventuell neu schreiben, da sich die systeminterne Logik geändert haben kann. Aus diesem Grund sollten Sie nie direkt schreibend auf SAP-eigene Tabellen zugreifen.
Die Abhilfe für dieses Dilemma schaffen sogenannte BAPIs. BAPIs sind vorgegebene Funktionsbausteine für wohldefinierte, häufig anzutreffende betriebswirtschaftliche Szenarien. Die Abkürzung steht für Business Application Programming Interface. Aus technischer Sicht sind BAPIs Funktionsbausteine, die bestimmten syntaktischen Regeln gehorchen.

Fachlich gesehen stellen BAPIs für Sie sicher, dass Datenbankinhalte nur auf konsistente Art und Weise modifiziert werden. Sie sollten also zuerst prüfen, ob für Ihren Zweck bereits ein BAPI existiert. Falls ja, sollten Sie es auf jeden Fall verwenden, selbst wenn Sie nur einen Teil seiner Funktionalität benötigen. Alle Alternativen sind wesentlich fehleranfälliger.
Auch wenn kein BAPI für Ihren Zweck existiert, können Sie mit einem Trick auf offiziell ausgelieferten Code zum Modifizieren von Daten zugreifen. Ein Mechanismus, der Batch Input genannt wird, gestattet es nämlich, die SAP-eigenen Oberflächen in Funktionsbausteine zu kapseln. Das Vorgehen, das andernorts auch als Screen Scraping bezeichnet wird, ist ebenso plump wie naheliegend. Sie zeichnen einen Bedienvorgang der passenden SAP-Transaktion als Marko auf. Das ist möglich, da jedes Feld einer SAP-Oberfläche einen innerhalb der Maske eindeutigen Namen trägt. So können Sie auf automatisierte Weise die Benutzereingaben in die Masken emulieren – inklusive dem Klick auf den Speichern-Button. Liegt der Vorgang einmal als Makro vor, wandeln Sie ihn in ABAP-Code, um dann die eingegebenen Werte nach Bedarf zu parametrisieren und als Funktionsbaustein aufzurufen.

Das Batch-Input-Verfahren ist allerdings anfällig gegenüber Änderungen an der Oberfläche oder Bedienvarianten. Beispielsweise kann ein Eingabefeld unter bestimmten Rahmenbedingungen ausgegraut sein. Falls Ihr Makro davon ausgeht, dass es immer veränderbar ist, wird es die Bearbeitung mit einem Fehler abbrechen müssen. Dennoch hat das Batch-Input-Verfahren seine Daseinsberechtigung. Wenn es für Ihren Zweck kein BAPI gibt, bietet Batch Input die einzige Möglichkeit, auf konsistente Weise SAP-eigene Tabellen zu ändern.

Ganz anders verhält es sich, wenn Sie lediglich die Inhalte von Tabellen ändern wollen, die Sie selbst angelegt haben. Dann haben Sie deren Konsistenz ganz in eigener Verantwortung. Sie dürfen und müssen unter diesen Umständen natürlich einen eigenen Funktionsbaustein zur Verfügung entwickeln.
Hier sind die soeben erklärten Regeln noch einmal als Stichpunkte zusammengefasst. Sie gelten für die SAP-Seite von RFC-Schnittstellen, wo-bei SAP die Rolle des Servers einnimmt.

  1. Für schreibende Zugriffe nutzen Sie soweit vorhanden BAPIs.
  2. Falls kein BAPI für Ihren schreibenden Zugriff existiert, nutzen Sie den Batch-Input-Mechanismus.
  3. Für schreibende Zugriffe auf selbstdefinierte Tabellen implementieren Sie selbst einen remote-fähigen Funktionsbaustein.
  4. Für lesende Zugriffe implementieren Sie selbst einen remote-fähigen Funktionsbaustein oder nutzen falls vorhanden einen vom System vorgegebenen.

Im Folgenden werden die einzelnen besprochenen Techniken ausführlicher vorgestellt.

6.1.2 Remote-fähige Funktionsbausteine

Damit ein selbstgeschriebener Funktionsbaustein über den RFC-Mechanismus aufrufbar wird, muss er ein paar Bedingungen erfüllen. Betrachten wir diesen minimalen Funktionsbaustein, der später auch für den ersten JCo-Zugriff verwendet wird.

Um ihn über RFC ansprechbar zu machen, muss man ihn als remote-fähig markieren. Dies geschieht im Function Builder bei den allgemeinen Attributen des Funktionsbausteins, wie in Kapitel 4 beschrieben wurde.
Sobald Sie einen remote-fähigen Funktionsbaustein aktivieren, wird eine Reihe von Hilfsfunktionen generiert und der SAP Application Server regiert ab sofort auf RFC-Aufrufe an diesen Baustein. Beim Aktivieren oder Speichern eines remote-fähigen Funktionsbausteins überprüft die ABAP-Umgebung eine Reihe von Bedingungen ab, die für unser Minimalbeispiel Z_EINFACH allesamt erfüllt sind. Bei einem komplexeren Funktionsbaustein, der über eine Reihe von Parametern verfügt, ist dies ein wenig komplizierter.

Bevor wir uns einem zweiten Funktionsbaustein Z_PARAMETER zu-wenden, der die erwähnten Schwierigkeiten auslotet, ist ein kurzer Exkurs über Speicherverwaltung im Allgemeinen notwendig. Wenn man sich überlegt, wie überhaupt Parameter von einem Prozess zum anderen oder gar von einem Rechner zum anderen gelangen können, werden die Restriktionen der RFC-Bausteine plausibel.

Bei Funktionsaufrufen, ganz egal in welcher Programmiersprache, unterscheidet man zwischen wertbasierter Parameterübergabe und referenz-basierter Übergabe. In ersterem Fall erhält das aufgerufene Programm eine Kopie des Parameters, in letzterem lediglich eine Referenz auf einen Speicherbereich, in dem der eigentliche Parameter liegt. Dieser Speicherbereich wurde von dem aufrufenden Programm vorbereitet und mit Inhalt gefüllt. Es gibt zwei Hauptgründe, um referenzbasierte Parameterübergabe zu nutzen: man verwendet Parameter variabler Länge oder man möchte sich einen Kopiervorgang ersparen.

In ABAP ist der Typ string der typische Vertreter für einen Typ variabler Länge. Der ABAP-Compiler weiß im Voraus nicht, wie viel Platz zur Laufzeit für einen string benötigt wird. Daher realisiert er ihn intern über einen Zeiger, dessen Größe natürlich feststeht und der auf einen Speicherbereich variabler Größe verweist. Funktionsparameter vom Typ CHANGING sind ein Beispiel für das Sparen eines Kopiervorgangs. Sie geben dieselbe Struktur in den Funktionsbaustein hinein, die dieser wieder zurückgibt. Sie mag zwar modifiziert werden, aber es handelt sich um das-selbe Exemplar einer Struktur. Dasselbe gilt für Parameter, die Sie als REFERENCE(…) und nicht als VALUE(…) deklarieren. Auch hier-bei spart man sich einen Kopiervorgang und operiert innerhalb der Funktion auf demselben Speicherbereich wie außerhalb.

Der Gedankengang schließt sich nun, wenn man sich klar macht, was mit einer Referenz passiert, die nicht lokal in einem Programm übergeben, sondern über einen RFC-Aufruf auf einen anderen Rechner übertragen wird. Auf dem anderen Rechner ist der Hauptspeicher gänzlich anders angeordnet als auf dem Ursprungsrechner. Daher hat die Referenz dort keinerlei sinn-volle Bedeutung. Vielleicht weist sie auf einen verfügbaren Speicherbereich, aber keinesfalls auf einen, in dem sich der erwartete Inhalt befindet.

Daher verbietet der ABAP-Compiler in remote-fähigen Funktionsbau-steinen jede Art von referenzbasierten Parametern. Das gilt nicht nur für die offensichtlichen Referenzen, die über REFERENCE(…) deklariert wurden, sondern auch für die versteckten Referenzen, also für CHANGING-Parameter und solche, die variable Länge haben wie string oder Strukturen mit string- oder expliziten Referenzfeldern. Zur Erinnerung: im Function Builder stellen Sie über eine Checkbox in der Spalte „Wertübergabe“ ein, dass ein Parameter per Wert übergeben wird. Um alle typischen und erlaubten Parametertypen eines remote-fähigen Funktionsbausteins abzudecken, betrachten wir die Definition von Z_PARAMETER.

Dieser Funktionsbaustein enthält IMPORTING- und EXPORTING-Parameter, die allesamt wertbasiert übergeben werden. Außerdem verwendet er eine Tabelle und eine Exception.
Versteckte Referenztypen sind nicht vorhanden, denn der Typ Z_C10 ist ein Datentyp äquivalent zu c(10), also von fester Länge 10. Auch die Struktur ZPARAMSTRUCT enthält, wie in Abb. 6.1 gezeigt, nur Felder fester Länge.


Abb. 6.1. Definition der Struktur ZPARAMSTRUCT im ABAP Dictionary

Merken Sie sich am besten die Feldnamen, denn wir werden später vom JCo aus auf diese Felder zugreifen.

Ein Wort noch zu dem einen TABLES-Parameter. In nicht remote-fähigen Funktionsbausteinen sind TABLES-Parameter unüblich, da Sie seit R/3-Release 4.5 auch unter IMPORTING, EXPORTING und CHANGING beliebige Strukturen und insbesondere Tabellen übergeben können. Sie müssen die Typdefinition lediglich unter einem eigenen Namen im ABAP Dictionary bekannt machen. Aus Konsistenzgründen nutzen Sie diese Möglichkeit auch, da so alle Parameterarten gleich behandelt werden und die Verwendungsrichtung immer ablesbar ist.

Die Implementierung der RFC-Bibliotheken hinkt der Entwicklung des R/3-Systems ein wenig hinterher. Daher werden von den Client-seitigen RFC-Bibliotheken zum gegenwärtigen Zeitpunkt noch keine Tabellenparameter außerhalb des TABLES-Abschnitts unterstützt. Da der Function Builder im R/3 auch für remote-fähige Funktionsbausteine Tabellen in den IMPORTING, EXPORTING und CHANGING-Abschnitten zulässt, müssen Sie selbst darauf achten, diese vorerst zu vermeiden.

Sie haben nun das Werkzeug in der Hand, um selbst remote-fähige Funktionsbausteine zu schreiben. Wie eingangs erläutert sollten Sie dies nur dann tun, wenn Sie keine potentiell konsistenzgefährdenden Datenmanipulationen vornehmen. Andernfalls sollten Sie auf BAPIs oder notfalls auf den Batch-Input-Mechanismus zurückgreifen. Rein technisch gesehen reduzieren sich beide Vorgehensweisen wiederum auf einen RFC-Baustein, der denselben Gesetzmäßigkeiten folgt, wie in diesem Abschnitt erörtert.

6.1.3 BAPIs

BAPIs sind viel mehr als nur eine besondere Art von Funktionsbaustein. BAPIs stellen den Versuch dar, das R/3-System sauber zu modularisieren und als Schnittstelle zu standardisieren. In der Frühzeit von R/3 war das System wild gewachsen mit dem Ergebnis, dass dieselbe Funktionalität gelegentlich doppelt implementiert wurde und nicht immer als solche erkennbar war. Das erschwerte die Wartung und machte tiefgreifende Strukturänderungen zu einem schwierigen Unterfangen.

Die BAPI-Technologie schafft an dieser Stelle Abhilfe. Ihr Hauptzweck ist es, das System in autarke Teile zu gliedern, die voneinander unabhängig sind und lediglich über wohldefinierte Schnittstellen aufeinander zugreifen. Die Umstellung auf die BAPI-Architektur ist ein langwieriger Prozess, der bis heute andauert.

Doch BAPIs dienen nicht nur der internen Modularisierung, sondern stellen auch fachlich wohldefinierte Zugriffspunkte nach außen dar. Der Entwickler kann dadurch auf zentrale Funktionalitäten über Schnittstellen zugreifen, die sich auch in zukünftigen Releases nicht ändern werden. Zumindest gilt dies, sobald das BAPI von SAP offiziell freigegeben wurde.

BAPIs folgen dem objektorientierten Paradigma, auch wenn Sie gröber granular sind als das typische Java-Objekt. Ein BAPI verfügt über mehrere Methoden, die im ABAP-Code über

angesprochen werden. Ganz ähnlich wie bei Java-Methoden gibt es instanzabhängige und instanzunabhängige Methoden. Die meisten BAPIs lassen sich über eine instanzunabhängige Create-Methode instanziieren oder über eine Find-Methode suchen. Auf der dadurch gewonnenen BAPI-Instanz kann man dann instanzabhängige Methoden ausführen. Außerdem verfügen BAPIs über Attribute und einen Vererbungsmechanismus.

Ein BAPI spielt die Rolle einer vom System mitgelieferten Enterprise Java Bean. Es kapselt die Geschäftslogik zum konsistenten Modifizieren von Datenbankinhalten für einen bestimmten Use Case.

Wenn Sie ein BAPI über den RFC-Mechanismus aufrufen, merken Sie wenig von dessen objektorientierter Natur. Das liegt daran, dass der RFC-Mechanismus streng prozedural ist. Um objektorientierte Methodenaufrufe auf Funktionen abzubilden, hat SAP jeder BAPI-Methode einen remote-fähigen Funktionsbaustein zugeordnet, der ungefähr dieser Namenskonvention folgt:

So entspricht der BAPI-Aufruf Vendor.GetInternalNumber dem Funktionsbaustein BAPI_VENDOR_GETINTNUMBER. Auch die Parameterliste des Funktionsbausteins kann geringfügig von der der zugehörigen

BAPI-Methode abweichen. Wenn Sie beispielsweise eine instanzabhängige BAPI-Methode aufrufen möchten, müssen Sie dem entsprechenden Funktionsbaustein zusätzlich im ersten Parameter die Objekt-ID der Instanz mit-geben. Anders könnten Sie den Bezug zu der speziellen Instanz nicht her-stellen. Durch dieses Nebeneinander der unterschiedlichen Notationen ist es auch nachvollziehbar, dass im allgemeinen Sprachgebrauch gelegentliche einzelne BAPI-Methoden selbst als BAPI bezeichnet werden.

Im R/3 können Sie über den BAPI-Explorer durch die verfügbaren BA-PIs navigieren. Sie erreichen ihn über die Transaktion BAPI. In Abb. 6.2 sehen Sie das BAPI Vendor, wie es im BAPI-Explorer dargestellt wird. Durch Doppelklick auf eine BAPI-Methode öffnen Sie das Fenster, das rechts unten sichtbar ist. So können Sie den Namen des entsprechenden Funktionsbausteins ermitteln.

Wenn Sie ein BAPI identifiziert haben, das Ihren Zwecken dient, und den Namen des zugehörigen Funktionsbausteins ermittelt haben, können Sie diesen wie jeden anderen RFC-Baustein verwenden.

Abb. 6.2. Das Vendor-BAPI im BAPI-Explorer

Ein paar Eigenheiten der BAPI-Funktionsbausteine sind dennoch wissenswert. So werden Sie feststellen, dass BAPI-Funktionsbausteine nie eine Exception in der Schnittstelle deklarieren. Stattdessen werden Sie in der Deklaration immer einen Exporting-Parameter finden, der ungefähr so geartet ist:

Die BAPIRET2-Struktur gibt Ihnen Auskunft darüber, ob der Aufruf erfolgreich ausgeführt wurde. Sie enthält weiterhin Meldungstexte über die Ausführung des BAPIs, die entweder rein informativ sein können oder aber eine Fehlersituation erläutern. Die wichtigsten Felder von BAPIRET2sind in der folgenden Liste aufgeführt.

Tabelle 6.1. Ausgewählte Felder der BAPIRET2-Struktur

Feld Typ Bedeutung
Type Char 1 E = Error
W=Warning
I = Information
A = Abort
S = Success oder System
ID Char 20 Meldungs-ID, oft bedeutet TYPE=S und ID=0, dass ein Fehler aufgetreten ist.
MESSAGE Char 220 Meldungstext
MESSAGE_V1 – MESSAGE_V4 Char 50 Variable Anteile der Meldung

Das Feld TYPE entscheidet darüber, ob die Struktur eine gutartige Meldung enthält (W, I) oder auf einen Fehler hinweist (E, A). Tückisch ist der Typ S, der meist für eine erfolgreiche Ausführung (Success) steht, gelegentlich aber auf Systemfehler hinweist. Letzteres tritt meist in Kombination mit der ID 0 auf. Oft gibt ein BAPI nicht nur eine einzige BAPIRET2-Struktur, sondern eine Tabelle solcher Strukturen zurück. Sie müssen daher alle Einzelmeldungen durchgehen und prüfen, ob Sie auf einen Fehler hinweisen, um sicherzustellen, dass der Aufruf als ganzer erfolgreich war.

Anstelle der BAPIRET2-Struktur geben manche BAPIs Strukturen vom Typ BAPIRET1 oder BAPIRETURN zurück. Dies sind Vorgängerversionen mit ähnlicher Semantik. Sie entsprechen der generellen Praxis von SAP, neuere Versionen eines ABAP-Objekts mit einer abschließenden Versionsnummer zu kennzeichnen. Diese Richtlinie kann auch auf ganze BAPIs oder BAPI-Methoden angewandt werden. Bevor Sie also BAPI_VENDOR_GETDETAIL aufrufen, sollten Sie überprüfen, ob nicht eventuell schon ein BAPI_VENDOR_GETDETAIL2 existiert. Letzterem sollten Sie den Vorrang geben, denn die Existenz einer neueren Version weist darauf hin, dass die ältere Version in zukünftigen Releases nicht mehr unterstützt wird. Schließlich ist der Leitgedanke beim BAPI-Einsatz die Langlebigkeit der Schnittstellen.

Das Anhängen von Versionsnummern an BAPI-Methodennamen entspricht dem Deprecation-Mechanismus in Java. Methoden mit niedrigeren Versionen im Namen werden in zukünftigen Releases nicht mehr unterstützt.

Ebenso wichtig für die Langlebigkeit der Schnittstellen ist ihr Freigabestatus. Im BAPI-Explorer können Sie unter den zentralen Eigenschaften eines BAPIs nachsehen, ob es von SAP freigegeben wurde. Nur dann ist sichergestellt, dass es in dieser Form lange erhalten bleibt. Nicht freigegebene BAPIs sollten Sie nur dann nutzen, wenn Sie keine anderen BAPIs finden, die Ihrem Zweck genügen. Zusammenfassend lässt sich sagen, dass BAPIs tatsächlich nur wie ein spezieller Typ von RFC-Baustein zu verwenden sind. Ihre Nutzung hat jedoch ihre Tücken und erfordert in der Praxis viel Sorgfalt und Geduld.

6.1.4 Batch Input / Call Transaction

Die Bezeichnung Batch Input steht ursprünglich für ein Verfahren, das die Eingabe von Massendaten in R/3-Oberflächen ermöglicht. Lang laufende oberflächenlose Prozesse werden ja oft als Batch-Prozesse bezeichnet. Wenn man vermeiden möchte, bei der Datenübernahme viele hundert Mal dieselbe Maske manuell auszufüllen, bietet das Batch-Input-Verfahren einen Ausweg. Es ermöglicht, den Eingabevorgang einmal in einem Makro-Editor aufzuzeichnen und anschließend parametrisiert mit den ge-wünschten Daten immer wieder abzuspielen.

Man kann den Batch-Input-Mechanismus aber auch zweckentfremden, um eine Oberfläche von einem Funktionsbaustein aus zu befüllen, wenn man kein BAPI findet, das die Funktion dieser Oberfläche kapselt. Wir wollen exemplarisch die Bedienung der Transaktion IE02 auf-zeichnen, um die Bezeichnung eines Equipments zu ändern. Den Transaktionsrecorder erreichen Sie über die Transaktion SHDB. Wenn Sie eine neue Aufzeichnung anlegen, müssen Sie dieser einen Namen geben und festlegen, welche Zieltransaktion aufgezeichnet werden soll. In unserem Fall ist das IE02.


Abb. 6.3. Beginn der Aufzeichnung im Transaktionsrecorder

Die Transaktion IE02 ist relativ simpel aufgebaut. Wir geben die Nummer eines Equipments ein, bekommen dessen Eigenschaften angezeigt, ändern den Beschreibungstext und speichern. Anschließend speichern wir ein zweites Mal, um die Transaktionsaufzeichnung abzuschließen. Das Ergebnis ist das Makro, das in Abb. 6.4 zu sehen ist. Es beginnt mit dem Namen der aufgezeichneten Transaktion und enthält unter anderem Einträge für jede Cursorbewegung (BDC_CURSOR). Außerdem sind zwei feste Werte erkennbar: die Equipment-Nummer, die in Zeile fünf in das Feld RM63E-EQUNR eingetragen wird und die geänderte Equipment-Bezeichnung. Sie wird in Zeile 24 in das Feld ITOB-SHTXT eingefügt.

Damit im Makro die festen Werte durch Variablen ersetzt werden können, müssen Sie den Makro-Code in ABAP-Code umwandeln. An dieser Stelle weichen wir von dem reinen Batch-Input-Verfahren ab, denn wir wollen ja keine Massendateneingabe mit Hilfe statischer Eingabedateien durchführen, sondern Eingabeparameter zur Laufzeit festlegen. Genau genommen heißt dieses abgewandelte Vorgehen „Call Transaction“, we-gen der großen Nähe ist der verbreitetere Begriff „Batch Input“ dennoch ebenso häufig auch in diesem Zusammenhang zu hören. Um den ABAP-Code zu erzeugen, navigieren Sie eine Ebene zurück, wählen aus der Liste der gespeicherten Makros das soeben aufgezeichnete Z_EQU aus und gehen in der Kopfleiste auf „Programm anlegen“. Dort vergeben Sie einen Programmnamen, z. B.Z_EQUNAME und wählen „aus Aufzeichnung übernehmen“.


Abb. 6.4. Makro-Code, der bei der Bedienung von IE02 aufgezeichnet wurde.

Nun können Sie sich den Quelltext des generierten ABAP-Programms ansehen. Mit ein paar Auslassungen sieht er so aus:

Jetzt ist es ein Leichtes, die festen Werte durch variable Parameter zu ersetzen und den Code in einen remote-fähigen Funktionsbaustein umzubetten.

6.2 RFC auf Java-Seite

Der Java Connector ist ein Produkt, das SAP ihren eingetragenen Kunden kostenlos zur Verfügung stellt. Wie erwähnt handelt sich dabei um eine Kapselung der RFC-Schnittstelle, die diese von Java-Code aus aufrufbar macht. Auch wenn es andere vergleichbare Produkte gibt, ist JCo die am weitesten verbreitete Lösung und soll hier stellvertretend für die Java-Seite des RFC-Aufrufs beschrieben werden.

Die folgenden Punkt charakterisieren die JCo-Bibliothek:

  • JCo erlaubt es, Client-seitigen Java-Code für den RFC-Zugriff zu schreiben.
  • Dazu ermöglicht JCo einen einfachen Verbindungsaufbau zum R/3-System. Nach Verbindungsaufbau kann man ein dynamisches API zum Auffinden und Ausführen der verfügbaren Funktionsbausteine nutzen, das ähnlich wie das Java Reflection API funktioniert.
  • Optional bietet JCo Connection Pooling an, um die Verbindungsressourcen zu schonen.
  • JCo wird in erster Linie für synchrone Aufrufe genutzt.
  • Mit dem JCo lässt sich auch Server-seitiger Java-Code schreiben. Der Java JCo-Server emuliert dadurch ein SAP-System und lässt sich von ABAP-Code aus aufrufen.
  • Da der JCo eine C-Bibliothek über das Java Native Interface (JNI) auf-ruft, ist er nicht geeignet, von einem J2EE-Server aus aufgerufen zu werden. JNI-Aufrufe sind in J2EE-Servern verboten, da sie die Thread-Synchronisation stören und die Systemstabilität gefährden.

Der letzte Punkt stellt eine schwerwiegende Einschränkung an die Verwendung des JCo dar. Dennoch war JCo lange Zeit die einzige Möglichkeit, von Java aus auf SAP zuzugreifen und hat daher durchaus seine Daseinsberechtigung. Die Netweaver-Umgebung, die im dritten Teil des Buches beschrieben wird, verfügt über eine J2EE-konforme Implementierung des JCo auf Basis der Java Connector Architecture (JCA). Die Lektüre dieses Abschnitts ist also auch dann nicht vergebens, wenn Sie den JCo nicht alleinstehend, sondern nur als Teil des Netweaver einsetzen wollen.

Der Java Connector eignet sich generell für den Aufruf von remote-fähigen Funktionsbausteinen. Die Unterscheidung in selbstgeschriebene Funktionsbausteine, BAPIs und Batch-Input-basierte Funktionsbausteine ist daher auf Java-Seite nicht notwendig.

6.2.1 Installation und Verbindungsaufbau

Wenn Sie SAP-Kunde sind, können Sie den JCo von http://services.sap.com/connectorsunentgeltlich herunterladen. Zur Installation müssen Sie die mitgelieferten DLLs bzw. Shared Libraries auf Ihren Rechner kopieren und über den Bibliothekspfad Ihres Betriebssystems erreichbar machen. Unter Unix ist dies die Umgebungsvariable LD_LIBRARY_PATH, unter Windows die PATH-Variable. Anschließend nehmen Sie die Java-Bibliothek sapjco.jar in den Classpath Ihrer Java-Entwicklungsumgebung auf. Sobald dies geschehen ist, können Sie den ersten Verbindungsaufbau zum SAP-System durchführen. Dazu genügt das folgende Codefragment, das einen Import von com.sap.mw.jco.* voraussetzt. Sie müssen im Code die Parameter desjenigen R/3-Systems eintragen, das Sie für den RFC-Aufruf nutzen wollen. Ihr SAP Basis-Administrator kann Ihnen die notwendigen Werte nennen. Wenn Sie den Code ausführen können ohne dass eine Exception auftritt, ist die Verbindung zum SAP-System geglückt.

Für den produktiven Betrieb werden Sie in der Regel nicht Ihren persönlichen User verwenden, sondern einen technischen User. Dieser wird Ihnen ebenfalls vom SAP Basis-Administrator zur Verfügung gestellt. Für erste Tests spricht aber nichts dagegen, den User zu verwenden, unter dem Sie sich auch im SAP GUI anmelden.

Die Fehlerbehandlung in dem kurzen Codeabschnitt bedarf noch einer Erläuterung. Die JCo-Klassen deklarieren generell keine geprüften Exceptions. Sie werden daher vom Compiler nicht zur Fehlerbehandlung gezwungen, was eigentlich unsauber ist. Stattdessen werfen die JCo-Klassen Exceptions, die von RuntimeException abgeleitet sind. Sie müssen also selbst darauf achten, diese zu behandeln. Sämtliche von JCo geworfenen Exceptions sind von JCO.Exception abgeleitet. Daher empfiehlt es sich, zumindest diese generell nach JCo-Aufrufen zu behandeln.

6.2.2 Auffinden und Aufrufen eines einfachen Funktionsbausteins

Als nächstes soll der fehlende Code für den eigentlichen RFC-Aufruf ein-gefügt werden. Der Übersichtlichkeit halber geschieht dies zunächst am Beispiel des minimalen Funktionsbausteins Z_EINFACH aus dem Ab-schnitt über remote-fähige Funktionsbausteine. Bevor Sie einen RFC an ein SAP-System starten, benötigen Sie Informationen über die dort vorhandenen Funktionsbausteine. Der JCo bietet als zentrale Auskunftsstelle für Metainformationen über den aufrufbaren Code ein Repository-Objekt an. Dieses lässt sich anhand der Session er-zeugen, denn die Session definiert das R/3-System eindeutig. Als zweiten Parameter geben Sie dem Konstruktor von JCO.Repository einen Namen mit, unter dem Sie ihn ansprechen wollen. Der folgende Code zeigt, wie man ein Repository-Objekt instanziiert und wie man anschließend die darin befindlichen Metainformationen verwendet, um den gewünschten Funktionsbaustein aufzurufen.

Die Art und Weise, wie hier ein JCO.Function-Objekt stellvertretend für die aufzurufende Funktion erzeugt wird, erinnert stark an das Reflection API von Java. Über repository.getFunctionTemplateermitteln Sie zu dem Funktionsnamen ein Funktionstemplate, das dessen Struktur repräsentiert. Bei einer produktiv genutzten Implementierung müssen Sie den Fall behandeln, dass diese Methode null zurückgibt, weil die gewünschte Methode nicht gefunden wurde. Aus dem Funktionstemplate können Sie ein JCO.Function-Objekt generieren, das wiederum über connection.execute ausgeführt wird.

6.2.3 Aufruf eines komplexen Funktionsbausteins

Die große Fleißarbeit bei der JCo-Programmierung besteht darin, die zu übergebenden Funktionsparameter korrekt zu befüllen und die zurückgelieferten Ergebnisstrukturen wieder auszulesen. Das Repository hilft Ihnen zwar, zur Laufzeit zu prüfen, ob der RFC-Baustein verfügbar ist. Dennoch müssen Sie im Voraus das Wissen darüber in Code gießen, wie er heißt und wie seine Parameter aussehen. Da dies nicht automatisch geschieht, ist es eine langwierige und fehlerträchtige Angelegenheit. Insofern ist das vorhergehende Beispiel alles andere als repräsentativ gewählt. Wir wollen die Mechanismen zum Setzen von Parametern anhand des zweiten Funktionsbausteins untersuchen, den wir vorbereitet haben. Der RFC-Baustein Z_PARAMETER enthält exemplarisch für jeden möglichen Typ einen Parameter. Dadurch kommen wir automatisch mit sämtlichen JCo-Aufrufen in Kontakt, die man in der Praxis beim Füllen von Parameterlisten benötigt. Zu Testzwecken erweitern wir den Rumpf von Z_PARAMETER so, dass er die Importparameter direkt an die Exportparameter weiterreicht.

Es folgt der Java-Code, mit dem man den so erweiterten Funktionsbau-stein aufruft. Er ist wiederum in das erste Listing an der Stelle zu ergänzen, an der die Auslassung gekennzeichnet ist.
Das JCO.Function-Objekt verfügt für jeden Parameterabschnitt wie IMPORTING über eine eigene Zugriffsmethode. Sie heißt beispielsweise getImportParameterList. Auf diesem Objekt wiederum kann man einfache Parameter direkt über setValue-Methoden setzen. Die set-Value-Methode ist anhand des zu setzenden Parametertyps überladen. Der zweite Parameter ist in jedem Fall der Feldname, der groß geschrieben werden muss. Strukturen wie den zweiten Import-Parameter setzt man über das von getStructure von der Importliste zurückgegebene Strukturobjekt. Die einzelnen Felder der Struktur sind wiederum über setValue zu erreichen. Der Codeabschnitt zum Befüllen der Tabellenparameter sollte verständlich sein. Dann folgt der Aufruf des Funktionsbausteins. Auf dem SAP-Server wird nun der oben aufgeführte ABAP-Code ausgeführt. Anschließend geht es weiter in diesem Listing. Die Export- und Tabellenparameter werden wieder ausgelesen. Da Methoden nicht anhand ihrer Rückgabeparameter überladen werden können, gibt es anstelle einer allgemein-gültigen getValue-Methode typspezifische Rückgabemethoden wie getString, getInt, etc.

Wenn nun alles wie gedacht funktioniert hat, sind die Ausgaben von export1, export2 und table1 identisch mit den Werten, die zuvor über import1, import2 und table1 gesetzt wurden.

Zur Fehlerbehandlung müssen Sie darauf gefasst sein, dass sämtliche Methoden, die über einen Feldnamen auf einen Parameter zugreifen, eine JCO.Exception werfen, falls dieser nicht vorhanden ist. Dieses Fehler-verhalten steht im Kontrast zu der null-Semantik von getFunction-Template. Außerdem werfen typspezifische get-Methoden wie get-Date eine Exception, wenn der vorhandene Feldwert nicht in diesen Typ gewandelt werden kann. Interessant ist noch das Verhalten bei Exceptions auf R/3-Seite. Dazu wird der Funktionsbaustein so abgeändert, dass er lediglich eine Exception wirft.

Tatsächlich lässt sich die Exception auf Java-Seite nicht durch irgendeine get-Methode des Funktionsobjekts abfragen. Stattdessen liefert die Ausgabe des catch-Blocks aus dem Rumpfprogramm diese

Java-Exception:com.sap.mw.jco.JCO$AbapException:
(126) EXCEPTION1: Fehler zum Testen

6.2.4 Problemstellungen aus der Praxis

In der Praxis werden Sie mit einer Reihe von Problemen konfrontiert, die über den reinen Gebrauch des JCo-APIs hinausgehen.

Wertebereiche von Parametern

Wo irgend möglich rufen Sie vom System mitgelieferte Funktionsbausteine auf. Diese nutzen oft Feldinhalte, die auf den R/3-Kontext abgestimmt sind. So verfügen viele Felder über einen eingeschränkten Wertebereich, der über die Domäne oder über separate Prüftabellen festgelegt wird. Be-sonders beim schreibenden Zugriff müssen Sie darauf achten, nur zugelassene Werte zu verwenden. Andere Felder besitzen eine kurze interne Darstellung und eine lange oder sprachabhängige Darstellung, die im SAP GUI angezeigt wird. An der BAPI-Schnittstelle bekommen Sie nur die interne Darstellung übergeben. Wenn Sie auf Java-Seite eine Benutzeroberfläche entwickeln, müssen Sie diese wiederum in verständliche Langdarstellungen umwandeln. Das Datumsformat aus R/3, das Sie über die getDate-Methoden des JCos abfragen, weist einige Inkonsistenzen auf, da es sowohl vom ABAP-Typ T als auch vom Typ D befüllt wird. Währungsfelder werden in SAP platzsparend so formatiert, dass die irrelevanten Stellen abgeschnitten werden. Wenn in einem Land also die kleinste Münze den Wert 1000 hat, entfallen drei Stellen in der Darstellung. Connection Pooling Sehr wahrscheinlich ist Ihr Java-Programm keine alleinstehende Applikation, sondern selbst ein Server. In diesem Fall müssen Sie sparsam mit den JCo-Verbindungen umgehen, da diese sich relevant auf den Ressourcen-haushalt auswirken. Der JCo bietet für diesen Zweck einen Connection Pool an. Die folgende Aufrufsequenz aus unserem Codebeispiel können Sie durch eine Pool-basierte Variante ersetzen.

Der äquivalente Code unter Verwendung des Connection Pools sieht so aus: Sie beschaffen sich ein Pool-Objekt, das mehrere Verbindungen zum R/3-System verwalten wird. Falls es noch nicht existiert, müssen Sie es anhand einer Properties-Datei initialisieren. Von diesem Pool erhalten Sie auf Anfrage über getClient eine einzelne Verbindung. Da alle Verbindungen des Pools auf dasselbe R/3-System zugreifen, wird das Repository für die Metadaten nicht über die Connection initialisiert, sondern über den Namen des Pools. Am Ende geben Sie die Connection an den Pool zurück, statt sie zu beenden.

Auf diese Weise reduzieren Sie die Anzahl der Verbindungsauf- und abbauten. Diese sind in der Regel recht aufwändig. Die Properties-Datei, die beim Initialisieren des Pools benötigt wird, hat den folgenden Aufbau. Die genauen Werte erfahren Sie wiederum von Ihrem SAP Basis-Administrator.

Da Sie das Connection Pooling nur dann einsetzen, wenn Sie mit mehreren Threads arbeiten, müssen Sie streng darauf achten, eine Connection nur in genau einem Thread zu verwenden. Auf keinen Fall dürfen Sie Verbindungen zwischen Threads vertauschen oder Verbindungen von mehreren Threads aus gemeinsam verwenden. Dann würden sich die unter-schiedlichen Threads die zu sendenden Inhalte inkonsistent überschreiben und unberechenbare Fehler wären die Folge.

In der Vergangenheit hat es allerdings auch JCo-Versionen gegeben, die von sich aus nicht Thread-sicher waren. Falls Sie eine solche in Händen hal-ten, werden Sie nicht umhin kommen, den Pool selbst zu implementieren.

Synchrone RFC

Wenn Sie sich für den Einsatz der RFC-Technik entscheiden, entscheiden Sie sich für eine synchrone Koppelung zweier unterschiedlicher Systeme. Das tut man in erster Linie dann, wenn man direkte Benutzerinteraktion hat. Wir unterstellen einfach, dass Ihr Java-Server dazu dient, für einen Anwender eine Oberfläche darzustellen. Wenn der Anwender auf einen bestimmten Button drückt, stößt der Java-Server einen RFC an. Der Anwender wartet so lange vor dem Bildschirm, bis das Ergebnis des RFC vom Java-Server empfangen und an ihn weitergeleitet wurde.

Synchrone Aufrufe haben einen Nachteil. Sie setzen voraus, dass beide Systeme gleichzeitig verfügbar sind und dass außerdem das Netzwerk zwischen beiden funktionsfähig ist. Mit solchen Konstellationen kann man die Ausfallsicherheit des Gesamtsystems schnell drücken. Drei synchron gekoppelte Systeme mit einer Ausfallsicherheit von passablen 99 Prozent ergeben ein Gesamtsystem mit einer Ausfallsicherheit von sehr mittelmäßigen 97 Prozent.

Ein konkreteres Problem ist das Lastverhalten. Wenn Sie eine synchrone Kopplung über RFC vornehmen, greifen auf der Strecke vom Browser des Endbenutzers bis zum Backend R/3-System eine Reihe von Timeouts. Der Browser des Anwenders wartet vielleicht eine oder ein paar Minuten auf eine Antwort vom Java-Server, die TCP/IP-Verbindung vom Java-Server zum R/3-Server hat ihren eigenen Timeout.
Bei Funktionsbausteinen mit rein lesenden Datenbankzugriffen stellt dies kein Problem dar. Bei zu übertragenden Datenmengen, die auf einen Bildschirm passen, braucht der RFC im Normalfall weniger als eine Sekunde. Anders sieht es aus, wenn Sie schreibend auf das R/3 zugreifen. Ein betriebswirtschaftliches Objekt anzulegen oder zu ändern erfordert eine Vielzahl von Änderungen in unterschiedlichen Tabellen. Wenn Sie gar eine ganze Liste von Objekten in einem RFC ändern wollen, vielleicht noch über das Batch-Input-Verfahren, kann die Ausführung viele Sekun-den dauern. In Situationen, in denen das R/3-System unter Last steht, be-deutet das, dass der Anwender lange auf eine Reaktion warten muss, was allein schon unerfreulich ist. Falls die Reaktionszeit des RFC aber einen der Timeouts überschreitet, erhält er eine Fehlermeldung vom Java-Server. Der Funktionsbaustein arbeitet im Hintergrund wahrscheinlich erfolgreich weiter, wovon der Benutzer nichts erfährt. Eventuell stößt er die Aktion erneut an, die er für fehlgeschlagen hält und erzeugt so Inkonsistenzen und noch höhere Last.

Die synchrone Kopplung eines Java-Servers mit einem R/3-System birgt also eine Reihe von Risiken, die man sich bewusst machen muss, bevor man diesen Weg einschlägt.

Es gibt aber eine Alternative, die fast so gut ist wie eine synchrone Kopplung. Sie können einen RFC aufspalten in einen synchronen kurzen Teil und einen asynchronen länger dauernden Teil. Der kurze Teil sollte die Semantik haben: „Der Aufruf wurde fehlerfrei entgegengenommen. Die Verarbeitung wurde angestoßen.“ Diese Meldung wird an das aufrufende System in den Rückgabeparametern des RFC zurückgegeben. Der asynchrone Anteil läuft dann noch im Hintergrund weiter und erledigt aufwändige Verbuchungsaktionen. Im Hinblick auf die Benutzerführung bietet es sich an, dem Anwender eine Funktion in die Hand zu geben, mit der er nachsehen kann, ob seine Verbuchung schon erfolgt ist. Technisch können Sie die Zweiteilung des Funktionsbausteins wie folgt realisieren.

Der RFC ruft den Funktionsbaustein Z_SYNCHRON auf. Dieser prüft, ob die eingegebenen Parameter vollständig und sinnvoll sind. Dies geht schnell und hilft, überflüssige Verbuchungsversuche zu vermeiden. Dann bereitet er die Rückgabemeldung für die vorläufige Erfolgsmeldung vor. Als drittes ruft er den Funktionsbaustein Z_ASYNCHRON mit dem Zusatz IN BACKGROUND TASK auf. Dadurch wird ein separater R/3-Task gestartet, der die lang laufende Verbuchungsaktion durchführt. Unter gewissen Umständen ist ein anschließendes COMMIT vonnöten, damit der gerufene Funktionsbaustein tatsächlich zu arbeiten beginnt. Der synchrone Funktionsbaustein ist nun schon beendet und gibt Rückmeldung an die aufrufende Seite. Der asynchrone Anteil arbeitet in Ruhe vor sich hin, bis er seine Aufgabe erledigt hat. Idealerweise protokolliert er den Erfolg oder Misserfolg der Aktion in einer Datenbanktabelle, die man später wieder auslesen kann. Eine weitere Alternative zum synchronen RFC ist die asynchrone IDoc-Technologie, die in Kapitel 7 behandelt wird.

6.3 Debugging

Es ist immer hilfreich, beim Entwickeln die notwendigen Werkzeuge zur Hand zu haben, um den auftretenden Fehlern wirklich auf den Grund zu gehen. Wir wollen den verfügbaren Werkzeugkasten kurz vorstellen.

Der Debugger der ABAP Workbench ist eines der Debugging-Hilfsmittel. Wenn man in seinem Funktionsbaustein einen Breakpoint setzt und dann darauf hofft, dass der JCo-Aufruf darin stoppt, wird man enttäuscht. Das Programm läuft ohne anzuhalten durch. Das liegt daran, dass Breakpoints immer nur für eine Benutzersitzung Gültigkeit haben. Andernfalls könnten sich ja unterschiedliche Benutzer oder unterschiedliche Personen, die unter demselben R/3-User arbeiten, gegenseitig die Programme anhalten.

Wenn Sie dennoch den Debugger aus der Workbench verwenden wollen, müssen Sie einen Trick einsetzen. Sie haben nämlich die Möglichkeit, über die Transaktion SM50 ein laufendes Programm in den Debugger zu holen, auch wenn es von einer anderen Benutzersitzung aus gestartet wurde. Das Problem ist nur, Sie sind nicht schnell genug, um Ihren Prozess zu treffen. Er ist ja meist in Sekundenbruchteilen abgearbeitet. Daher behelfen Sie sich mit einer Endlosschleife am Anfang Ihres Funktionsbausteins. Die kann so aussehen und sollte auf jeden Fall eine Abbruchbedingung enthalten:

Der Programmablauf stockt also in der Schleife, auch wenn Sie den Funktionsbaustein über den RFC-Mechanismus von Ihrem Java-Programm aus aufrufen. Dadurch gewinnen Sie die Zeit, die Sie benötigen, die Transaktion SM50 aufzurufen. In Abb. 6.5 sehen Sie einen Ausschnitt aus dieser Oberfläche. Sie können den vom JCo gestarteten R/3-Prozess anhand des Benutzernamens und der Funktionsgruppe identifizieren.


Abb. 6.5. Laufende ABAP-Programme aus Sicht der Transaktion SM50

Nun wählen Sie Ihren Prozess aus und wählen in der Kopfleiste „Programm/Modus / Programm / Debugging“. Dann öffnet sich der Debugger. Sie sehen die gerade ausgeführte Codezeile und können sich unten auch die Inhalte lokaler Variablen anzeigen lassen. Abb. 6.6 zeigt diese Situation.


Abb. 6.6. Debugger mit geänderter Ausgangsbedingung für die Endlosschleife

Um den Funktionsbaustein nun aus der Endlosschleife zu befreien, lassen Sie sich die Variable flag anzeigen. Diese dient ja als Abbruchbedingung für die Endlosschleife. Nun ändern Sie den Wert von flag und können dann im Einzelschrittmodus die eigentliche Debug-Sitzung beginnen. Auf keinen Fall sollten Sie vergessen die Endlosschleife zu entfernen, bevor Sie Ihren Code zum Transport freigeben. Ein viel einfacheres, aber dennoch sehr nützliches Hilfsmittel bei der Fehlersuche ist die writeHTML-Methode des JCo-Funktionsobjekts. Sie können sich damit den vollständigen Inhalt des JCo-Aufrufs in eine HTML-Datei schreiben lassen. Dazu genügt der folgende Aufruf direkt nach dem Ausführen des RFC-Bausteins.

connection.execute(function);
function.writeHTML(„C:\\temp\\rfc-parameter.html“);

Als letztes Werkzeug sollten Sie den Tracing-Mechanismus über die Transaktion TS05 kennen. Dort können Sie das Tracing für ein bestimmtes Protokoll – in unserem Fall RFC – aktivieren. Anschließend führen Sie einen zu tracenden RFC-Aufruf durch und deaktivieren das Tracing wie-der. Die Ausgaben sind allerdings nur von begrenztem Nutzen. Sie dienen in erster Linie zu Profiling-Zwecken, da jeweils die Ausführungsdauer in Millisekunden angegeben wird. Außerdem können Sie dort die Verbindungsparameter ablesen, und den Code des aufgerufenen Funktionsbausteins ansehen. Aber dies sind eigentlich Informationen, die Sie ohnehin schon besitzen.

6.4 Weiterführende Themen

Bisher sind wir davon ausgegangen, dass das R/3-System immer die Rolle des Servers bei einem RFC-Aufruf spielt. Das war lediglich eine Reduzierung auf den typischen Fall. RFC-Aufrufe können sehr wohl auch von einem SAP-System in ein anderes SAP-System erfolgen. Außerdem können Sie über RFC auch einen Nicht-SAP-Server ansprechen. Unabhängig davon, wie der RFC-Server nun geartet ist, erfolgt der RFC-Aufruf von einem ABAP-Client aus über CALL FUNCTION ‚Funktionsname‘ DESTINATION ‚RFC-Destination‘. Der Unterschied zu einem lokalen RFC-Aufruf liegt also nur in der DESTINATION-Klausel. Diese verweist auf die so genannte RFC-Destination, ein Systemeintrag für mögliche Ziele von RFC-Aufrufen. RFC-Destinations lassen sich über die Transaktion SM59 verwalten. Dort werden in erster Linie technische Verbindungsparameter auf den logischen Namen abgebildet, den die DESTINATION-Klausel referenziert. Wir wollen kurz skizzieren, wie die Kommunikation zwischen einem ABAP-Client und einem Java-Server über RFC aussehen kann. Dazu gehen wir davon aus, dass die RFC-Destination JCOSERVER bereits über die Transaktion SM59 konfiguriert wurde. Der ABAP-Client ruft über diese Destination die Funktion JCOSERVER_FUNCTION auf. Sie verfügt nur über einen Import- und einen Exportparameter. Nach dem Aufruf wird der erhaltene Exportparameter ausgegeben.

Der Java-Server ist von JCO.Server abgeleitet. Er nutzt dessen Konstruktur, um ihn mit den notwendigen Verbindungsparametern zu füllen. Diese sind in Konstanten abgelegt und werden der Einfachheit halber hart kodiert übergeben. Das Hauptprogramm übernimmt die mühselige Aufgabe, das Repository mit Metadaten zu füllen. Dies geschieht natürlich nicht automatisch, da unser Java-Server ja nur ein SAP-System emuliert. Am Ende des Hauptprogramms wird ein Server-Objekt instanziert und über die start-Methode zum Laufen gebracht. Wann immer nun ein RFC-Aufruf an diesen Server gerichtet wird, nimmt die Basisklasse JCO.Server ihn entgegen. Sie bereitet ihn so weit auf, dass sie ihn an die handleRequest-Methode in Form eines JCO.Function-Objekts weitergeben kann. In unserer eigenen Implementierung von handleRequest geben wir lediglich den Importparameter aus und füllen den Exportparameter mit dem Text „Antwort“.

Auf Basis dieser etwas knappen Codeskizze sollten Sie in der Lage sein, einen vollständigen RFC-Server zu implementieren. Ihnen kommt dabei zugute, dass Sie sich nicht an vorhandene umständliche Schnittstellen anpassen müssen. Den Server schreiben schließlich Sie, dann können Sie auch bestimmen, wie die Schnittstelle aussieht. Als Wermutstropfen bleibt aber die Mühsal beim manuellen Füllen des Repositorys.

7. IDocs

Schon früh in der Entwicklungshistorie von SAP entstand der Bedarf nach einem Format zum Datenaustausch zwischen SAP- und Fremdsystemen oder auch zwischen unterschiedlichen SAP-Systemen. Es sollte in erster Linie den Schriftverkehr zwischen verschiedenen Unternehmen in Papier-form ersetzen. Das Ergebnis war das Intermediate Document, kurz IDoc. Das IDoc-Format erfüllt eine Reihe von Anforderungen, die es zum genügsamen Lastesel im Datenaustausch machen und ihm eine große Verbreitung bescheren.

Dies sind die wichtigsten Eigenschaften von IDocs:

  • IDocs eignen sich zum asynchronen Datentransfer, da sie als Byteformat definiert sind und nicht an ein Transportprotokoll gekoppelt sind. Sie können somit als Files zwischengelagert, per Mail versandt oder über Message Queues verschickt werden.
  • IDocs enthalten Informationen über den Absender und das Zielsystem. Dadurch kann trotz zeitverzögerter Bearbeitung sichergestellt werden, dass sie den gewünschten Adressaten erreichen.
  • IDocs verfügen über eine mehrfach geschachtelte Struktur, die zum Übertragen von Listen oder optionalen Feldern geeignet ist.
  • IDocs enthalten eine eindeutige Typbezeichnung, die als Verweis auf ihre Formatdefinition dient. So wird die Deutbarkeit der Daten jederzeit sichergestellt. Es gibt eine große Menge von standardisierten IDocs, mit deren Hilfe sich die meisten Kommunikationsvorgänge durchführen las-sen. Bei Bedarf können Sie aber auch neue IDocs selbst definieren oder bestehende erweitern.
  • Jedes IDoc enthält eine eindeutige Nummer, die genau dieses eine Exemplar identifiziert. Dadurch lässt sich Mehrfachverarbeitung vermeiden und nachvollziehen, welche Bearbeitungsstationen es erreicht hat.
  • IDocs führen eine Bearbeitungshistorie mit, die protokolliert, welche Prozessschritte sie im SAP-System durchlaufen haben.

IDocs ähneln XML-Dokumenten in zweierlei Hinsicht: Sie haben eine geschachtelte Struktur und sie enthalten einen Verweis auf ihre eigene Strukturdefinition.

Zu der grundsätzlichen Entscheidung, ob Sie einen synchronen oder einen asynchronen Kommunikationsmechanismus einsetzen wollen, sei noch ein-mal auf das vorhergehende Kapitel verwiesen. Unter der Überschrift „Synchrone RFC“ werden dort ein paar Risiken synchroner Aufrufe zwischen zwei unterschiedlichen Systemen diskutiert. Diese Risiken drohen bei der IDoc-Technologie nicht. Wenn Sie nicht darauf angewiesen sind, dass Ihre Daten sofort von einem System zum anderen gelangen, sollten Sie in Erwägung ziehen IDocs einzusetzen. Die dadurch entstehende Systemlast lässt sich leicht auf betriebsarme Zeiten verschieben. IDocs setzen außerdem nicht voraus, dass beide Systeme zum gleichen Zeitpunkt verfügbar sind.

7.1 Aufbau eines IDocs

7.1.1 Physikalische Struktur

Ein IDoc besteht aus drei Teilen: dem Kontrollsatz, den Datensätzen und dem Statussatz. Abb. 7.1 verschafft Ihnen eine erste Anschauung dieser Teile. Sie zeigt ein Exemplar eines IDocs aus Sicht der Transaktion WE09.


Abb. 7.1. Physikalische Struktur eines IDoc-Exemplars

Zweck des Kontrollsatzes ist einerseits, das IDoc anhand von IDoc-Nummer und -Typ zu identifizieren. Andererseits enthält der Kontrollsatz auch die Informationen über den Absender und Empfänger des IDocs. Der Kontrollsatz hat eine feste Länge.

Die eigentlichen Nutzdaten sind in einem oder mehreren Datensätzen enthalten. Ein Datensatz setzt sich aus einem kurzen Anteil fester Länge mit Verwaltungsinformationen und 1000 Bytes an Nutzdaten zusammen. Die Verwaltungsinformation besteht aus einem Rückverweis auf das IDoc über seine Nummer und einer Typisierung der Nutzdaten. Ein Datensatz enthält nämlich genau eine logische Struktur, Segment genannt. Damit das Segment richtig gedeutet wird, muss sein Typ bekannt sein. Daher wird dieser wie bereits angedeutet in den Verwaltungsinformationen des Datensatzes festgehalten. Zur Unterscheidung der Segmente gleichen Typs im selben IDoc fließt außerdem eine Segmentnummer in die Verwaltungsinformationen ein. Der logischen Struktur der Nutzdaten auf Ebene der Segmente werden wir uns gleich noch ausgiebig zuwenden. IDocs sind immer als Teil eines Workflows zu verstehen. Sie enthalten nicht nur Daten, sondern sind gleichzeitig zur Verarbeitung in unterschiedlichen Teilschritten in einer vorgegebenen Reihenfolge vorgesehen. Aus diesem Grund wird der gegenwärtige Verarbeitungsstatus eines IDocs im Statussatz vermerkt. Auch der Statussatz hat eine feste Länge.


Abb. 7.2. Schemadarstellung der physikalischen Struktur eines IDocs

7.1.2 Logische Struktur

Für die Verarbeitung eines IDocs ist die logische Struktur wichtiger als die darunter liegende physikalische Struktur. Die Dreiteilung des IDocs und die 1000-Byte-Restriktion sind unter dem Blickwinkel der logischen Struktur nur ein technisches Realisierungsdetail. Das logische IDoc setzt sich schlicht aus einer Reihe von aufeinanderfolgenden Segmenten zusammen. Ein Segment besteht aus einzelnen Feldern. Es ist also mit einer ABAP-Struktur vergleichbar und lässt sich sogar über das ABAP Dictionary ansehen. Jedes Segment hat einen wohl definierten systemweit bekannten Typ. Über den IDoc-Typ ist festgelegt, von welchen Typen dessen Segmente sind, in welcher Reihenfolge sie auftreten und wie oft Segmente desselben Typs auftreten dürfen.

Der IDoc-Typ legt weiterhin eine mögliche Schachtelung von Segmenten in Form von Vater-Kind-Beziehungen fest. Ein IDoc-Typ für eine Bestellung besteht beispielsweise aus einem Segment für den Auftragskopf. Dieses darf nur genau einmal vorkommen. Darunter liegen beliebig viele Segmente für je eine Auftragsposition. Die Schachtelungsvorschrift ist keine Eigenschaft des Segmenttyps Auftragskopf, sondern des IDoc-Typs für die Bestellung. Dadurch ist gewährleistet, dass dieselben Segmenttypen in ganz unterschiedlichen IDoc-Typen verwendet werden können.

In Abb. 7.3 ist zur Veranschaulichung die Segmentstruktur des IDocs ORDERS05 dargestellt. Sie erkennen gut die Segmente der ersten Ebene. Über das Pluszeichen kann deren Struktur weiter expandiert werden. Diese Sicht auf die Definition beliebiger IDocs erreichen Sie über die Transaktion WE60.


Abb. 7.3. Logische Segmentstruktur des IDocs ORDERS05

7.1.3 Benennung von IDocs und Segmenten

Ein zentraler Gedanke der IDoc-Technologie ist der, dass das System eine große Sammlung standardisierter IDocs für eine Vielzahl von Verwendungszwecken zur Verfügung stellt. Dadurch wird sichergestellt, dass der Code zur Verarbeitung desselben Inhalts nicht mehrfach geschrieben wer-den muss. Außerdem wird durch standardisierte IDocs eine gemeinsame Basis für den Datenaustausch zwischen ansonsten disjunkten IT-Systemen geschaffen.

Das Dilemma einer eingefrorenen weil standardisierten Typisierung ist, dass das Format nicht an neue Anforderungen angepasst werden kann. SAP hat eine recht elegante Lösung dieses Problems gefunden. Die Evolution standardisierter IDoc-Segmente funktioniert durch eine Trennung von Typ und aktuellem Format.

Der Name eines IDocs besteht aus dem eigentlichen Namen und einer angehängten Versionsnummer. So ist das IDoc ORDERS03 die dritte Version vom Typ ORDERS. Eine neue Version entsteht jeweils nur durch Erweiterung aus der Vorgängerversion. Dadurch bleibt der ABAP-Code zum Verarbeiten des IDocs auch dann funktionsfähig, wenn er auf eine neuere Version desselben IDoc-Typs angewendet wird. Im Gegensatz zur Evolution von BAPI-Definitionen bedeutet die Existenz einer neueren IDoc-Version nicht, dass die alte nicht mehr unterstützt wird.

Bei der Bezeichnung von Segmenten wird noch klarer zwischen generellem Typ und versionierter Segmentdefinition unterschieden. Der Segmenttyp von SAP-eigenen Segmenten beginnt mit E1. Zu einem Segment-typ E1XXX existiert zunächst eine konkrete Definition des Segmentformats namens E2XXX. Die Segmentdefinition ist versionsabhängig, der Segmenttyp nicht. Sollten also neue Versionen der Segmentdefinition benötigt werden, so erhalten sie die Namen E2XXX001, E2XXX002, etc. Doch auch die neuen Versionen werden als zugehörig zum Segmenttyp E1XXX betrachtet. Eine neue Segmentdefinition kann ebenfalls nur durch Zufügen von neuen Feldern aus der Vorgängerversion hervorgehen, nicht aber durch Entfernen oder Ersetzen.

Noch einmal in Kürze: abstrakte Segmenttypen fangen mit E1 an, konkrete Segmentdefinitionen mit E2. Letztere enden mit einer dreistelligen Versionsnummer, sofern es sich nicht um die nullte Version handelt. Wie auch bei vielen anderen ABAP-Objekten ist der Anfangsbuchstabe Z für benutzerdefinierte IDocs und Segmente reserviert. Bei Segmenten wird zusätzlich das Typisierungsschema beibehalten. Benutzerdefinierte Segmenttypen beginnen also mit Z1, benutzerdefinierte Segmentdefinitionen mit Z2.Sämtliche Werkzeuge zum Ansehen, Versenden oder Definieren von IDocs finden Sie unter dem Pfad „Werkzeuge / Business Communication / IDoc-Basis“. Die meisten für IDocs relevanten Transaktionen beginnen mit WE.

7.2 Austausch von IDocs

Wir werden nun in mehreren Teilschritten die Kommunikation mit einem Fremdsystems mittels IDocs erläutern. Bei dem Fremdsystem handelt es sich in unserem Fall natürlich um ein Java-Programm. Um alle Aspekte des Kommunikationsvorgangs zu behandeln, soll ein IDoc von SAP aus an das Java-Programm gesendet werden und von dort wieder zurück. Dazu wird zunächst auf SAP-Seite das IDoc zusammengestellt und in der Datenbank abgespeichert. Ein Extraktionsmechanismus kopiert es dann in das Filesystem. Da es für die Weiterverarbeitung auf Java-Seite irrelevant ist, wie das IDoc dorthin gelangt, gehen wir davon aus, dass es dort wiederum in einer lokalen Datei vorliegt. In der Praxis schaltet man einen Kommunikationsmechanismus wie Mailversand oder eine Message Queue dazwischen. Das Java-Programm parst das IDoc und greift exemplarisch auf einige Felder zu.

Für die Rückrichtung erzeugt das Java-Programm ein IDoc und schreibt es ins Filesystem. SAP liest die Datei und wertet sie mittels ABAP-Code aus.

In Abb. 7.4 ist der gesamte Vorgang graphisch dargestellt. Wir werden ihn entgegen dem Uhrzeigersinn durchlaufen. Beim Export aus SAP und beim Import nach SAP ist in der Graphik jeweils ein kleiner Pfeil mit der Bezeichnung Rückmeldung eingetragen. An diesen Stellen wird der Me-chanismus zur Fehlerbehandlung angedeutet, aber nicht in voller Konse-quenz bis zum Absender ausgeführt.


Abb. 7.4. Skizze des durchzuführenden IDoc-Austauschs

Wie bereits erwähnt existieren für die meisten Verwendungszwecke, die eine Kommunikation mit einem Fremdsystem notwendig machen, standardisierte IDocs. Auch die Export- und Importmechanismen sind für solche Fälle vorgegeben. Sie ersparen sich viel Arbeit, wenn Sie auf diese etablierten Mechanismen zurückgreifen. Selbst wenn Sie nicht alle Felder eines Standard-IDocs benötigen, sollten Sie es einer Eigenimplementierung vorziehen.

In den nächsten Absätzen wird dennoch skizziert, wie Sie den Aus-tausch von IDocs auch für Eigenentwicklungen implementieren können. SAP nutzt dieselben Mechanismen für standardisierte IDocs. Daher tragen die Ausführungen auch dann zum Verständnis des Vorgangs bei, wenn Sie ihn nicht selbst implementieren wollen. Der Java-seitige Anteil der Aus-führungen ist in jedem Fall für Sie relevant. Für unser Kommunikationsbeispiel verwenden wir einen handlichen fiktiven IDoc-Typ, der zur Pizzabestellung genutzt wird. Es trägt den Namen ZPIZZA und besitzt nur die Ausprägung ZPIZZA. ZPIZZA besteht aus zwei Segmenttypen: Z1BESTELLER und Z1GERICHT. Z1BESTELLER

muss genau einmal vorkommen, während die Anzahl der Z1GERICHT-Segmente beliebig ist. Zwischen Z1BESTELLER und Z1GERICHT besteht eine Vater-Kind-Beziehung. Der Einfachheit halber soll Z1BESTELLERlediglich über die Felder NAME und TELEFON verfügen. Z1GERICHT wiederum besteht aus NAME und ANZAHL. Ein IDoc vom Typ ZPIZZA könnte also folgendermaßen dargestellt werden:

Bevor wir gleich den Code für die Pizzabestellung schreiben, soll noch eine Warnung ausgesprochen werden. Die Kommunikation über IDocs ist ein Vorgang, der tief mit der Administration des SAP-Systems verwoben ist. Wir gehen davon aus, dass Sie in einem Projekt die Rolle des Entwick-lers haben, nicht aber die des SAP Basis-Administrators. Daher beschrei-ben wir in erster Linie den Java- und ABAP-Code, der für die Kommuni-kation notwendig ist. Die administrativen Aktionen, die außerdem notwendig sind, um den Prozess zum Laufen zu kriegen, werden nur skiz-ziert. Sie werden ohnehin intensiv mit einem Administrator zusammenar-beiten müssen, wenn Sie eine IDoc-Schnittstelle in Betrieb nehmen.

7.2.1 Erzeugen eines IDocs in SAP

Anstoßen der IDoc-Erzeugung

Auf R/3-Seite wird ein IDoc in einem Funktionsbaustein erzeugt, dessen Name mit IDOC_OUTPUT beginnt. Er bekommt als Eingabe den Verweis auf ein beliebig geartetes Dokument im System und erzeugt daraus eine IDoc-Struktur, die er in einer Datenbanktabelle für das Abschicken bereit-stellt.Es kann unterschiedliche Anlässe geben, die ein IDoc erzeugen und deshalb diesen Funktionsbaustein anstoßen. Mögliche Auslöser dafür sind:

  • ein Aufruf aus einem User Exit,
  • ein Aufruf über die Nachrichtensteuerung oder
  • ein manueller Aufruf.

Ein typisches Beispiel für das Auslösen eines IDocs über einen User Exit ist die Replizierung bestimmter Stammdaten. Wenn Sie etwa in einem Fremdsystem die Lieferantenadressen zum SAP-System synchron halten wollen, gehen Sie diesen Weg. Sie nutzen einen User Exit, der vom System ausgeführt wird, sobald sich eine Adresse geändert hat. In diesem fügen Sie einen Aufruf von IDOC_OUTPUT… entweder direkt ein oder stoßen ihn über IDOC_MASTER_DISTRIBUTE indirekt an. Sie können aber auch aus einem beliebigen anderen Arbeitsablauf ein IDoc auslösen, indem Sie sich der sogenannten Nachrichtensteuerung bedienen. Deren Aufgabe ist es, die Erzeugung eines Dokuments wie etwa einer Rechnung von seiner Verschickung zu entkoppeln. Über die Nachrichtensteuerung können Sie festlegen, unter welchen Umständen die Rechnung auf Deutsch oder auf Englisch verschickt werden soll und welcher Kunde seine Rechnungen per Mail, per Post oder per IDoc entgegen-nimmt. Die Nachrichtensteuerung ist ein recht mächtiges, aber aufwändig zu bedienendes Instrument. Daher sei hier nur auf die Transaktionen NACE und NACO hingewiesen, die zu seiner Nutzung dienen. Es ist aber durchaus üblich, die IDoc-Erzeugung auf diesem Weg zu initiieren. Schließlich können Sie zu Testzwecken die IDoc-Erzeugung auch manuell anstoßen. Dies geschieht über die Transaktion WE19. Auch dadurch wird derselbe Funktionsbaustein ausgeführt.

Funktionsbaustein zum Erzeugen des IDocs

Genau genommen lautet der Name des Funktionsbausteins zum Erzeugen eines IDocs IDOC_OUTPUT_XXX, wobei der Anteil XXX stellvertretend für den Namen des IDocs steht. Falls es sich dabei um ein Standard-IDoc handelt, existiert der Funktionsbaustein bereits. Wenn Sie dagegen das IDoc selbst definiert haben, müssen Sie auch den Baustein selbst implementieren. Dabei empfiehlt es sich, den Namen mit Z_IDOC_OUTPUT beginnen zu lassen. In beiden Varianten ist das Grundprinzip dasselbe. Die Signatur sieht folgendermaßen aus:

Um die Bedeutung des ersten Parameters OBJECT zu verstehen, muss man sich in Erinnerung rufen, dass jedes zu erzeugende IDoc letztlich aus Daten zusammengesetzt wird, die sich bereits im SAP-System befinden.
Daher wird in OBJECT-OBJKY ein Schlüssel für den Datensatz mitgegeben, anhand dessen das IDoc gefüllt werden soll. In CONTROL_RECORD_IN wird der eingangs beschriebene Kontrollsatz des IDocs übergeben. Im Normalfall wird der Funktionsbaustein ihn soweit möglich auffüllen und dann in CONTROL_RECORD_OUT übergeben. In der Tabelle INT_EDIDD wird schließlich das vom Funktionsbaustein erzeugte IDoc als eine Liste von Datensätzen zurückgegeben, die die Segmente enthalten. Das System speichert diese Inhalte in einer nicht über das ABAP Dictionary einsehbaren Datenbanktabelle zwischen, um sie später als Quelle für den Export des IDocs zu verwenden. Wir wollen nun eine rudimentäre Implementierung dieses Funktionsbausteins nahtlos fortsetzen und sie dabei gleich abschnittsweise erläutern. In realen Szenarien wird die Implementierung wesentlich umfangreicher ausfallen. Es folgen zunächst einige Konstantendefinitionen. Sie dienen später dazu, den Namen des IDocs und der Segmente sauber typisiert zu setzen. Außerdem werden exemplarisch zwei Hilfsstrukturen definiert, die jeweils vom selben Typ sind wie die Segmente des IDocs, und eine (S_INT_EDIDD), die einem allgemeinen untypisierten 1000-Byte-Daten-satz entspricht.

Die eigentliche IDoc-Erstellung beginnt mit dem Kontrollsatz. Dazu wird CONTROL_RECORD_OUT initialisiert und der übergebene CONTROL_RECORD_IN hineinkopiert. Anschließend lassen sich die noch ungesetzten Parameter mit Hilfe der zuvor definierten Konstanten setzen. Sender- und Empfängerinformationen wie Partnerart und Port bleiben jedoch unberührt. Die IDOC_OUTPUT–Funktion hat nur die Aufgabe, den Inhalt des IDocs entsprechend der Strukturdefinition aufzubauen und mit Daten zu füllen. Für den Versand sind spätere Prozessschritte zuständig.

Als nächstes nutzt man den Schlüssel, der im OBJECT-Parameter über-geben wurde, um die Nutzdaten vorzubereiten, die man verschicken möchte. Dieser Schlüssel ist in der Regel Primärschlüssel einer Tabelle, so dass Sie den betreffenden Datensatz eindeutig ermitteln können. Er wird Ihnen vom auslösenden User Exit oder über die Nachrichtensteuerung mitgeliefert. Auf beiden Seiten muss also Übereinstimmung darüber bestehen, auf welche Tabelle er sich bezieht. Hier im Funktionsbaustein legen Sie sich durch eine solche Abfrage auf die konkrete Tabelle fest.

Natürlich sind meist komplexere Verarbeitungsvorgänge und womöglich auch mehrere Abfragen notwendig, um die Nutzdaten richtig aufzubereiten.
Nun werden die Segmente des IDocs manuell zusammengesetzt. Dazu müssen Sie natürlich dessen Struktur genau kennen. Man fügt sie einfach eines nach dem anderen in eine Liste ein. Da die IDoc-Struktur auch auf Empfängerseite bekannt sein wird, reicht die Reihenfolge der Segmente aus, um die gesamte Struktur wiederherzustellen. Für eine Vater-Kind-Beziehung wird erst das Vatersegment eingefügt und danach die Kindsegmente.
Um ein einzelnes Segment einzufügen, füllt man zuerst die segmentspezifische Hilfsstruktur. Anschließend weist man diese zusammen mit der Konstante für den Segmenttyp an die allgemeine Datensatzstruktur S_INT_EDIDD für ein Segment zu. Das Feld S_INT_EDIDD-SDATA ist der am Anfang des Kapitels beschriebene Block von 1000 Byte Nutzdaten. Dann wiederum kann S_INT_EDIDD an die Rückgabetabelle INT_EDIDD angehängt werden. Dies ist die erwähnte Liste der Segmente.
So verfährt man sukzessive für alle Segmente. In der Realität werden die Feldinhalte nicht hart codiert wie hier, sondern dynamisch aus dem Ergebnis des vorhergehenden Selects ermittelt. Dieses hatte ja zum Zweck, die Nutzdaten zu beschaffen, die im IDoc verpackt werden sollen.

7.2.2 Exportieren des IDocs aus SAP

Sobald der Funktionsbaustein IDOC_OUTPUT… ausgeführt wurde, erscheint das neue IDoc in der Datenbank. Sie können es über Transaktion WE02 ansehen.

Es ist die Aufgabe Ihres SAP-Administrators sicherzustellen, dass es darüber hinaus physikalisch exportiert wird. Dies geschieht über eine ALE (Application Link and Enabling) genannte Schicht des SAP-Systems. Die ALE-Schicht fungiert für den Entwickler als Black Box. Für Sie sind zur Orientierung lediglich zwei Parameter relevant, die das Ziel des Exports definieren: der Port und der Partner, an den das IDoc übermittelt werden soll.

Der Port legt den physikalischen Ausgang fest. Ein Port kann beispiels-weise ein Verzeichnis auf Betriebssystemebene sein, in das IDocs exportiert werden. Sie können aber auch Mail- oder RFC-Ports definieren, also Ausgänge an einen bestimmten Mailempfänger oder einen Funktionsbau-stein in einem anderen SAP-System. Der Partner wiederum wird zum Bestimmen des Ports herangezogen. Aus dem Glossar in Kapitel 2 wissen Sie vielleicht noch, dass ein Partner eine natürliche Person oder Unternehmenseinheit ist, mit der Geschäftsbeziehungen unterhalten werden. Typische Partner sind Kunden oder Lieferanten. Die Information, welche IDocs an welche Partner über welche Ports übertragen werden, lässt sich über die Transaktion SALE warten.

Für unseren Fall gehen wir davon aus, dass das IDoc in ein bestimmtes Verzeichnis exportiert wird, also in einen Datei-Port. Typischerweise werden dabei mehrere IDocs gleichen Typs in eine Datei geschrieben. Auch wenn ein IDoc exportiert wurde, verschwindet es nicht aus dem SAP-System. Es bleibt weiterhin in der Datenbank erhalten und ist über die eindeutige IDoc-Nummer identifizierbar. Das ist allein deswegen wichtig, weil in der weiteren Verarbeitung Fehler auftreten können, die ein erneutes Versenden notwendig machen.

Das Programm, das die exportierten IDocs weiterverarbeiten soll, sieht in periodischen Abständen im Exportverzeichnis nach, ob neue Dateien angelegt wurden, und verarbeitet diese. Die fertig verarbeiteten Dateien löscht es. Im Idealfall gibt es auch eine Rückmeldung über den Verarbeitungsstatus an das SAP-System. Dazu kann es den Funktionsbaustein EDI_STATUS_INCOMING aufrufen und ihm den Namen der verarbeiteten Datei zusammen mit einem Statuscode melden.

Da sich Funktionsbausteine nicht ohne weiteres von außen aufrufen lassen, bietet sich als Hilfsmittel das Programm startrfc an. Das Programm startrfc wird mit dem SAP GUI mitgeliefert. Seine Aufgabe ist es, Funktionsbausteine von Betriebssystemebene aus aufzurufen.

7.2.3 Einlesen des IDocs in Java

Wir gehen nun davon aus, dass das IDoc durch einen nicht näher definierten Übertragungsmechanismus auf der Java-Seite angekommen ist und als Datei vorliegt. Dort müssen wir nun das IDoc aus der Datei in den Speicher lesen und anschließend seine Felder parsen.

Einlesen der IDoc-Datei

Das Einlesen kann mit Code von dieser Gestalt geschehen. Er verwendet hauptsächlich die Klassen File und FileInputStream aus dem Java-Standardpaket java.io. Damit der Code kompakt bleibt, verzichten wir hier und im Folgenden darauf Exceptions zu behandeln. Für den produktiven Einsatz dagegen ist eine angemessene Fehlerbehandlung zwingend notwendig.

Bevor wir den Inhalt der bytes auswerten können, müssen noch einige Vorarbeiten geleistet werden. Sobald dies geschehen ist, werden wir an dieser Stelle fortfahren.

Vorbereitungen zum Parsen Zum Parsen des IDocs nutzen wir eine Reihe von Hilfsklassen, die nichts anderes tun als die Bytes konform zum IDoc-Format abzuzählen. Ihr voll-ständiger Code ist im Anhang aufgeführt. Er ist recht leicht verständlich und so weit erprobt, dass Sie ihn wahrscheinlich nicht anfassen müssen. Für Sie ist interessanter, wie Sie die Hilfsklassen verwenden. Dies ist eine kurze Übersicht über die Klassen und deren Bedeutung. Sie liegen alle im Package de.springer.javasap.idoc.

Klasse Funktion
BaseRecord Basisklasse für Strukturen mit einer Menge benann-ter Einzelfelder von fester Länge
Attribute Einzelfeld mit Namen und Länge
IDocObject Allgemeine Struktur eines IDocs
IDocHeaderRecord Struktur des IDoc-Headers
IDocSegmentRecord Allgemeine Struktur eines IDoc-Segments

Tabelle 7.1. Übersicht über die verwendeten Hilfsklassen und ihre Funktion

Klasse Funktion BaseRecordBasisklasse für Strukturen mit einer Menge benannter Einzelfelder von fester Länge AttributeEinzelfeld mit Namen und Länge IDocObjectAllgemeine Struktur eines IDocs IDocHeaderRecordStruktur des IDoc-Headers IDocSegmentRecordAllgemeine Struktur eines IDoc-Segments Die Klassen sind so geschrieben, dass sie die IDoc-Satzstruktur in der Version 4.0 unterstützten. Diese ist seit dem SAP-Release 4.0 gültig. Um ein IDoc mit dem hier vorgeschlagenen Mechanismus parsen zu können, müssen Sie zunächst dessen Struktur in maßgeschneiderten Klas-sen festhalten. Beginnen wir mit einem Segment. Es wird durch eine eige-ne Klasse abgebildet, die von BaseRecord abgeleitet ist.

Sie erkennen die Konstantendefinitionen für den Segmentnamen und die Länge des Segments. Letztere ist wegen der technischen Vorgabe bei Segmenten immer 1000. Die eigentliche Strukturdefinition wird in dem Array attrs festgehalten. Dieses ist aus mehreren Einträgen vom Typ Attribute zusammengesetzt. Jeder davon entspricht einem Feld des Segments. Die ersten beiden Konstruktorparameter von Attribute le-gen den Feldnamen und die Feldlänge fest. Damit ist die Struktur des Segments ausreichend spezifiziert. Die übrigen Methoden dienen zum Initialisieren des Objekts.
Da das Segment Z1GERICHT ganz ähnlich aufgebaut ist, gehen wir da-von aus, dass es in der Klasse SegmentZ1GERICHT analog abgebildet wird.

Als nächstes benötigen wir eine Strukturdefinition für das ganze IDoc.

Diese Klasse ist nicht von einer der Hilfsklassen abgeleitet. Stattdessen aggregiert sie alle benötigten Strukturen. Das sind einerseits die beiden Segmentklassen, die wir oben selbst definiert haben. Zum anderen verfügt sie über ein Attribut idocHeader, das die Kopfstruktur des IDocs abbildet. Die entsprechende Klasse IDocHeaderRecord ist wiederum eine der Hilfsklassen.
Gefüllt werden Header und Segmente über den Konstruktor, der einen Parameter vom Typ IDocObject auswertet. Der Typ IDocObject ist eine allgemeine IDoc-Struktur, die keine typspezifischen Eigenheiten enthält. Sie kann nur Segmentgrenzen erkennen, nicht aber deren Feinstruktur. Die genaue Verwendung dieses Konstruktors wird gleich im Anschluss erläutert. Sie können aber auch so erkennen, dass er aus dem übergebenen Roh-IDoc die Segmente extrahiert und ihren Typ prüft. Handelt es sich um einen der beiden erwarteten Typen, so wird die Nutzlast des Segments in die Attribute segmentZ1BESTELLER bzw. segmen-teZ1GERICHT übertragen. Letzteres ist ein Vektor von Segment-Z1GERICHT-Einträgen, denn Segmente dieses Typs können ja mehrfach vorkommen.
Da die Segmente nun mit Inhalt gefüllt sind, können die getMethoden im unteren Teil der Klasse auf einzelne Attribute zugreifen und sie zurück-liefern.

Parsen der eingelesenen IDoc-Datei

Jetzt sind die Bausteine beisammen, um eine ganze IDoc-Datei zu parsen, die zuvor aus SAP exportiert wurde. Zu Anfang des Kapitels wurde ihr Inhalt bereits in das Byte-Array bytes eingelesen. Mit diesen Zeilen können wir sie nun auch auswerten.

Eine Hälfte der Arbeit erledigt die Methode getIDocs aus der Hilfs-klasse IDocObject. Sie extrahiert alle IDocs ohne Ansehen des Typs aus dem Byte-Array und überträgt sie in einen Vektor. In einer Schleife kann man nun mit jedem dieser noch untypisierten IDocs den Konstruktor von IDocZPIZZA befüllen. Das ist natürlich nur möglich, wenn man sicher sein kann, dass die Datei nur IDocs von diesem Typ enthält. Der Konstruktor parst wie wir wissen die Segmente aus dem rohen IDoc und füllt sie in Attribute um. Nun kann man die get-Methoden nutzen, um den Wert einzelner Felder auszulesen. Da mehrere Gerichte in einer Pizzabestellung enthalten sind, benötigt man eine weitere Schleife, um die Rückgabe von getGerichte auszuwerten. Man kann auch die geerbten Methoden der IDoc-Klassen verwenden und beispielsweise über getDocNumdie Nummer des IDocs auslesen. In realen Einsatzszenarien werden Sie sicher mit komplexeren IDoc-Strukturen konfrontiert, als es hier der Fall war. Sie müssen mehrfache Schachtelungen behandeln und optionale Segmente berücksichtigen. Auch die Auswertung des Kontrollsatzes haben wir noch vernachlässigt. Dies alles bedarf natürlich noch einiger Programmierarbeit. Mit den vorgestellten Werkzeugen sollten die Probleme aber mit überschaubarem Aufwand zu meistern sein.7.2.4 Erzeugen eines IDocs in Java Setzen der IDoc-Inhalte Für die Rückrichtung verwenden wir dieselben Strukturdefinitionen wie-der. Wir möchten ein leeres IDoc instanzieren können, das wir dann feld-weise füllen. Dazu benötigen wir einen weiteren Konstruktor in IDocZ-PIZZA und passende set-Methoden. Im folgenden Codeauszug wird der schon vorhandene Code der Klasse nur angedeutet, lediglich die neuen Anteile sehen Sie vollständig.

Der neue parameterlose Konstruktor instanziert eine leere Header-Struktur und eine leere Segmentstruktur für den Besteller. Dadurch wird ein neues IDoc benutzbar gemacht, ohne dass man den Inhalt über ein Byte-Array mitgeben müsste, wie es der andere Konstruktor verlangt. Allerdings muss dieser zweite Konstruktor noch ein paar Parameter im Header setzen. Der IDoc-Typ und der Nachrichtentyp sind unabdingbar, damit das empfangende SAP-System das IDoc identifizieren kann. Sie werden über die Attribute MESTYP und IDOCTYP gesetzt. Als Richtung muss im Feld DIRECT der Wert 2 für „Eingang“ eingetragen werden, auch wenn das IDoc aus Java-Sicht hinausgeht. Für die Richtung ist nur die Sicht des SAP-Systems wichtig. Unter Umständen muss außerdem das Feld für die Nachrichtenvariante namens MESCOD gesetzt werden. Das ist für unsere Eigenentwicklung ZPIZZA nicht relevant. Wenn Sie aber standardisierte IDocs verschicken, kann es nötig sein. Die Feldinhalte der Segmente lassen sich über die neuen set-Methoden bzw. die Methode addGericht füllen. Um sicherzustellen, dass die Felder von SAP richtig gedeutet werden können, werden sie intern mit der Methode setAttrAsTrimmedString gesetzt. Diese füllt den Feldinhalt entweder rechts mit Leerzeichen oder links mit Nullen auf. Je nach-dem, ob der dritte Parameter anzeigt, dass der Inhalt numerisch (true) oder alphanumerisch (false) ist.

Setzen der technischen Parameter Zusätzlich werden beim Versenden auch einige technische Parameter benötigt, die das SAP-System braucht, um die Nachricht korrekt einzuordnen. Da diese Parameter im IDoc-Header abgelegt werden, müssen die zugehörigen set-Methoden auf die Header-Struktur zugreifen, statt auf eines der Segmente. Somit wird die Klasse IDocZPIZZA noch um die folgenden Methoden erweitert:

Diese Setter erlauben Ihnen, für Sender und Empfänger die Parameter Port, Partner und Partnerrolle einzustellen. Falls Sie weitere Parameter im Header an Ihre Bedürfnisse anpassen wollen, verfahren Sie einfach analog zu dieser Implementierung. Die Namen der vorhandenen Parameter können Sie der Klasse IDocHeaderRecord entnehmen. Erzeugen des IDocs im Byteformat Jetzt fehlt nur noch eine Methode, die aus den gesammelten IDoc-Daten das IDoc im Byteformat erzeugt. Dazu fügen wir zu IDocZPIZZA noch eine export-Methode hinzu.

Die Methode export erzeugt sich ein IDocObject-Objekt, wie es auch schon beim Auslesen eines IDocs unbekannten Typs genutzt wurde. Außerdem erzeugt sie ein temporäres IDocSegmentRecord-Objekt, das für die unterschiedlichen Segmente immer wiederverwendet wird. Dessen Segmentnummer wird über das SEGNUM-Attribut des Headers jeweils neu vergeben, beginnend mit 1. Das SEGNAM-Attribut wird auf den Namen des Segmenttyps gesetzt, über HLEVEL-Attribut legt man die Schachtelungstiefe fest. Die Schachtelungstiefe ist beim Besteller-Segment, das ja auf höchster Ebene liegt, gleich eins. Als nächstes füllt die export-Methode den Dateninhalt des Segments in Form der bekannten SDATA-Struktur von 1000 Byte Länge in das IDocSegmentRecord-Objekt.

Dessen gesamter Inhalt wiederum wird dann mit addSegment zu dem temporären IDoc-Objekt hinzugefügt. Für die Gerichtsegmente geschieht dasselbe in einer Schleife, allerdings ist die Schachtelungstiefe hier immer zwei. Abschließend wird der IDoc-Inhalt mit der Methode getByteArray zurückgegeben, die alle gesetzten Informationen in das Byteformat eines IDocs wandelt.

Aufruf der vorbereiteten Klasse Mit einer so erweiterten IDocZPIZZA-Klasse lässt sich ganz einfach ein IDoc generieren. Sie instanzieren das Objekt mit dem parameterlosen Konstruktor, setzten technische und fachliche Parameter und können dann über die export-Methoden die entsprechende Bytestruktur gewinnen.

Das fertige IDoc schreiben Sie ähnlich wie beim Einlesen über die Standard File-Klasse von Java in eine Datei.

In realen Szenarien werden Sie allerdings nie dieselben Daten in beide Richtungen schicken. Dieses Beispiel dient also nur zum Verdeutlichen der technischen Möglichkeiten.

7.2.5 Importieren des IDocs nach SAP

Wie schon auf dem Hinweg gehen wir davon aus, dass die IDoc-Datei auf nicht näher benannte Weise in das Eingangsverzeichnis des SAP-Servers gelangt.

Dann können Sie entweder über startrfc oder auf andere Weise den Funktionsbaustein EDI_DATA_INCOMING ausführen und den Namen der IDoc-Datei als Parameter mitgeben. Dieser liest die Datei ein, parst sie und ruft entsprechend dem Typ der darin enthaltenen IDocs den passenden verarbeitenden Funktionsbaustein auf. Sie sollten auch die Rückgabeparameter von EDI_DATA_INCOMING auswerten, denn diese geben Aufschluss über eventuell aufgetretene Fehler bei der Verarbeitung.

7.2.6 Auswerten des IDocs in SAP

Wie auch beim Erzeugen eines IDocs in SAP erfolgt das Auswerten des eingelesenen IDocs über einen Funktionsbaustein. Dieser trägt analog zum

Ausgabebaustein den Namen IDOC_INPUT_… oder Z_IDOC_INPUT… je nachdem, ob er sich auf ein benutzerdefiniertes IDoc bezieht. Auch die Signatur des Bausteins ist vorgegeben.

Zum Verständnis der Funktionsweise sind die TABLES-Parameter am wichtigsten. In der Tabelle IDOC_CONTRL werden die IDoc-Kontrollsätze von allen eingelesenen, aber noch nicht verarbeiteten IDocs übergeben. In der Tabelle IDOC_DATA sind die dazugehörigen IDoc-Datensätze enthalten. Als Ergebnis der Verarbeitung liefert der Funktions-baustein in IDOC_STATUS die aktualisierten IDoc-Statussätze zurück. Auch die Tabelle RETURN_VARIABLE und der Exportparameter WORKFLOW_RESULT liefern weitere Informationen über den Erfolg der Verarbeitung zurück.Die Aufgabe des Funktionsbausteins ist es also, die eingegangenen I-Docs in IDOC_CONTRL und IDOC_DATA zu parsen und deren Inhalte in der Datenbank zu verwahren. Dies geschieht im Wesentlichen in einer langen doppelten Schleife über alle IDocs und deren Segmente.

Für jedes eingegangene IDoc wird erst einmal der Typ geprüft. Falls es sich nicht um den erwarteten handelt, wird eine Exception ausgelöst. Dann werden seine Segmente durchlaufen und in einer CASE-Verzweigung anhand des Typs unterschieden. Sobald der Segmenttyp bekannt ist, kann man dieses den entsprechend typisierten Hilfsvariable S_BESTELLER bzw. S_GERICHT zuweisen. Über die Hilfsvariable ist ein Zugriff auf die einzelnen Segmentfelder möglich. Stellvertretend für einen komplexeren realen Verarbeitungsvorgang schreiben wir im Beispiel die ausgelesenen Felder einfach in eine nicht näher benannte Datenbanktabelle.

Till Jeske „SAP für Java-Entwickler“