10.10.2013 Aufrufe

WebAppServer_1B.pdf - DHBW Mosbach

WebAppServer_1B.pdf - DHBW Mosbach

WebAppServer_1B.pdf - DHBW Mosbach

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Stateful und Stateless BSP-Anwendungen: HTTP ist stateless !<br />

Stateful BSP-Programmierung :<br />

Anwendungskontext über Response hinaus<br />

gehalten ⇒ Bei Weiterführung zur Vfg.<br />

NWAS "merkt" sich Daten, die zu Anwendung<br />

gehören (Rollbereich), greift bei Fortsetzung<br />

wieder darauf zu:<br />

Klassisches R/3: Bei jedem SAPGUI-Bildwechsel<br />

NWAS: Daten auch nach Senden der Response-<br />

Seite an Browser für weitere Requests derselben<br />

BSP-Anwendung zur Vfg ⇒ Session Stickness !<br />

Nachteil :<br />

- Hohe Last auf dem Server - Kontextdaten vieler User im Speicher halten<br />

- Ressourcen unnötig lange gehalten, wenn User BSP-Anwendung abbricht (Timeout)<br />

Vorteil :<br />

User 1<br />

Session 1<br />

User 2<br />

Session 2<br />

User 3<br />

Session 3<br />

NWAS: stateful<br />

session<br />

session<br />

- Einfachere Programmierung - Daten bleiben zur Vfg (keine Cookies etc nötig!)<br />

- Keine erneute Datenbeschaffung + wiederholte DB-Zugriffe ! (Performanz !!)<br />

session<br />

Ressource Laufzeit vs. Ressource Speicher<br />

HTTP ist ein zustandsloses Protokoll: Nach Antwort eines Servers auf die Anfrage eines Clients hält<br />

der Server keine Zustandsinformationen über den Client - jede neue Anfrage des Clients erfordert<br />

einen neuen Verbindungsaufbau und erneute Datenbeschaffung.<br />

Jedoch wird in einer stateful BSP-Anwendung der Anwendungskontext über die Response<br />

(Serverroundtrip) und alle Benutzerinteraktionen hinweg gehalten, bis die BSP-Anwendung beendet<br />

ist. Der Anwendungs- / Benutzerkontext wird wie bei einer klassischen R/3-Transaktion im<br />

Rollbereich zwischengespeichert und wieder in den zuständigen WAS-Workprozess hineingerollt,<br />

wenn die Bearbeitung fortgesetzt wird. Der Anwendungskontext wird über alle Request-Response-<br />

Zyklen einer BSP-Anwendung gehalten - die Daten stehen auf jeder Seite über die gesamte Session<br />

zur Vfg.<br />

Konsequenzen:<br />

Auf dem Server wird eine große Last erzeugt: Web-Anwendungen werden meist von vielen<br />

Benutzern eingesetzt. Für jeden Benutzer wird Kontext auf dem Netweaver Application Server<br />

gehalten und belastet somit dessen Speicherkapazität.<br />

Ressourcen werden unnötig lange auf dem Server gehalten: Webbrowser melden sich nicht beim<br />

Server ab, wenn ein "flüchtiger" Benutzer zu einer anderen Seite navigiert. Eine offene Sitzung<br />

kann somit nicht beendet werden - und verbleibt im Speicher des WAS bis der Timeout-<br />

Mechanismus den Kontext freigibt. Da dies einige Zeit dauern kann, werden begrenzte Server-<br />

Speicherressourcen unnötig lange blockiert. Ist der Server nicht mehr in der Lage, weitere Daten zu<br />

speichern, verweigert er die Erstellung zusätzlicher Sessions, so dass kein weiterer Anwender die<br />

Applikation nutzen kann.<br />

Die Programmierung ist jedoch leichter zu realisieren: Die Daten stehen beim erneuten Zugriff<br />

ohne erneute DB-Abfragen für die komplette BSP-Session zur Vfg. Die Zahl der Datenbankzugriffe<br />

ist minimal. Es kann auf Cookies oder Hidden Fields verzichtet werden. Man sollte sich jedoch stets<br />

fragen, ob es wirklich nötig ist, die komplette Benutzersitzung permanent aufzubewahren.<br />

(1)<br />

Zeit


Session- Handling: SAP-Session- ID<br />

HTTP arbeitet stateless ⇒ Kennt nur unabhängige Requests<br />

BSP-LZ generiert Session-ID<br />

in Session-Cookie: sap-contextid<br />

-Imstateful-Fall verschickt<br />

Nach Response wird Verbindung geschlossen<br />

⇒ Kein Mechanismus zur Sessionkennzeichnung :<br />

Zuordnung mehrerer Request zu einer gemeinsamen Sitzung<br />

- Identifikation mehrerer Requests als Teil einer logischen Session (Anwendung)<br />

- Gültig bis zum Sitzungs-Ende<br />

- Abfrage über autom. instantiiertes Objekt runtime<br />

In allen Eventhandlern + Layout<br />

Attribut session_id abgreifbar<br />

Explizites Beenden einer Session :<br />

Data: s_id TYPE string .<br />

s_id = runtime→session_id .<br />

navigation→exit( 'http://www.sap.com' ) .<br />

Bewirkt im stateful-Fall den Abbau des Session-Kontextes + Freigabe aller Ressourcen<br />

(2)<br />

Funktioniert nicht, wenn in Browser-<br />

Einstellungen Sitzungs-Cookies nicht<br />

zugelassen sind !!<br />

Zum Session-Handling dient das globale, automatisch instantiierte Objekt runtime vom Typ<br />

IF_BSP_RUNTIME. Über runtime hat man Zugriff auf das Attribut session_id. Dieses enthält einen<br />

eindeutigen Sitzungsschlüssel zur Sitzungsidentifikation. (Eine weitere nützliche Methode des<br />

runtime-Objekts ist get_url(). Diese liefert die URL der aktuellen BSP-Seite zurück.)<br />

Allerdings wird nur im stateful-Fall dieser Sitzungsschlüssel in Form eines Sitzungs-Cookies an den<br />

Client-Browser geschickt (und von diesem mit dem nächsten Request der gleichen Session wieder an<br />

den Server).<br />

Eine BSP-Session ist der Zeitraum zwischen Start der Applikation durch Aufruf der entsprechenden<br />

URL, und Beenden der Applikation. Die Zuordnung von Requests zu einer stateful-BSP-Session<br />

erfolgt mit Hilfe von Session-Cookies, die von der BSP-Laufzeit erzeugt werden. Sie werden<br />

clientseitig als "sap-contextid" im Speicher abgelegt und bestehen auf dem Client nur für den<br />

Zeitraum der BSP-Session. Damit ist es der BSP-Laufzeit möglich, den Client-Requests eine<br />

eindeutige Session-Nummer anzufügen, um den Request im stateful-Fall einer bestimmten Session<br />

und dem Applikationskontext zuzuordnen. Bei jedem erstmaligem Aufruf einer neuen BSP-Anwendung<br />

wird eine neue session_id vergeben.<br />

Eine stateful-BSP-Anwendung hält Daten, die der Anwender eingegeben oder angefragt hat<br />

(Applikations- / Benutzerkontext), über die komplette BSP-Session hinweg. Der benutzer-spezifische<br />

Applikationskontext steht über die einzelnen Request-Response-Zyklen hinaus zur Vfg und wird von<br />

Seitenaufruf zu Seitenaufruf aktualisiert.<br />

Leider wird die Synchronität zwischen Client und Server durch das Stateful-Zustandsmodell<br />

gefährdet und muss vom Entwickler sichergestellt werden: Der Zustand der Anwendung kann sich<br />

von der Anzeige im Browser unterscheiden, zB durch Navigieren in der Browser-Historie oder mittels<br />

Browser-Back-Button. Ein User kann zu einer Webseite zurück navigieren und diese erneut senden,<br />

obwohl die Anwendung mit anderen Anfragen rechnet:


Stateless BSP-Anwendungen<br />

Nach Absenden jeder Response wird aktueller<br />

Benutzerkontext abgebaut und alle Ressourcen<br />

freigegeben ⇒<br />

- Alle Instanzen gelöscht<br />

- Alle Seitenattribut-Werte gelöscht<br />

- Alle Anwendungsklassen-Attributwerte<br />

gelöscht.<br />

Werte bei nächstem Request nicht mehr zur Vfg.<br />

Server ohne "Gedächtnis" - bedient isolierte<br />

Requests.<br />

Kompletter Kontextneuaufbau bei jedem<br />

Seitenwechsel erforderlich.<br />

1. Seitenattribute<br />

User 1<br />

User 2<br />

User 1<br />

In OnInitialization oder Layout wird Wert zugewiesen.<br />

In OnInputProcessing verwendbar, Wert dort aber wieder initial<br />

2. Attribute des Applikationsklassen-Objekts<br />

WAS : stateless<br />

response<br />

request<br />

response<br />

request<br />

request<br />

response<br />

Attribute (Einzelwerte und ganze interne Tabellen) nach Response in OnInputProcessing wieder initial<br />

Das Stateless-Zustandsmodell hält den benutzerspezifischen Applikationskontext nur für die Dauer<br />

eines Request-Response-Zyklus. Eine BSP-Anwendung stateless auszuführen bedeutet, daß für<br />

jeden Request ein neuer Applikationskontext erzeugt wird und einschließlich sämtlicher Instanzen<br />

nach jeder Response wieder verworfen wird (zustandslos = "gedächtnis-frei"). Ist der Request<br />

bearbeitet und die Response an der Browser gesendet, werden alle Ressourcen freigegeben. Der<br />

WAS verhält sich somit so, als würde sich jeder Benutzer einer BSP-Anwendung bei jedem<br />

Seitenwechsel abmelden und anschließend erneut anmelden.<br />

Der Kontext (Inhalt Seitenattributen und die Instanz der Anwendungsklasse mit ihren Attribut-Werten)<br />

bleibt nicht über einen Request- / Response-Zyklus hinaus auf dem WAS erhalten - und muss somit<br />

bei jedem folgenden Request komplett neu aufgebaut werden.<br />

Beispiel: Man weist in einer stateless-BSP einem Seitenattribut im Layout einen Wert zu. Dann kann<br />

zwar das Seitenattribut im Eventhandler OnInputProcessing verwendet werden, sein Wert ist dort<br />

aber wieder initial! Das gleiche gilt für die Attribute der Applikationsklasse.<br />

Anmerkung: Mit Ablauf des Benutzerkontextes wird auch das ABAP-Memory abgebaut, so dass die<br />

entsprechenden ABAP-Anweisungen:<br />

IMPORT ... FROM MEMORY ID ... und EXPORT ... TO MEMORY ID ...<br />

nicht sinnvoll einsetzbar sind.<br />

Das Verwerfen des Kontextes lässt sich auch in der SAP-Benutzerliste SM04 erkennen: Während<br />

des Aufrufs einer BSP-Anwendung erscheint kein Eintrag in der Benutzerliste. Erst wenn man zB in<br />

einem Eventhandler einen Beakpoint setzt, erscheint die Sitzung in der Benutzliste! Wird die gleiche<br />

Anwendung stateful geschaltet, so wird der Benutzkontext gehalten und führt zu einen Eintrag in der<br />

Benutzerliste.<br />

(3)<br />

Zeit


Stateless BSP-Anwendungen<br />

Nachteil :<br />

- Daten neu beschaffen, wenn in mehreren BSPs einer Anwendung benötigt<br />

- Erneute Datenbeschaffung und DB-Anfragen - oder: Cookies .....<br />

Vorteil :<br />

- Server Ressourcen nur während Request-Bearbeitung beansprucht.<br />

⇒ Kein Blockieren unnötiger Ressourcen<br />

- Bessere Skalierung des Servers - gut für viele Anwender<br />

- Browser-Back/Forward-Navigation unproblematisch<br />

- Direkter Sprung in beliebige Anwendungsseite unproblematisch<br />

Einstellen des Zustandsverhaltens :<br />

In Eigenschaften der BSP-Anwendnung kann stateful oder stateless selektiert werden!<br />

Selektives Umschalten zur LZ möglich - gemischte BSP-Anwendung :<br />

Setzen des Attributs keep_context des Objekts runtime :<br />

Umschalten auf stateful : runtime→keep_context = 1 .<br />

Umschalten auf stateless : runtime→keep_context = 0 .<br />

..... im Eventhandlercode ...<br />

Requests<br />

(4)<br />

Keine "Session Stickness" bei<br />

verteiltem SAP-System aus<br />

mehreren NWAS ⇒<br />

Jeder einzelnen Request kann von<br />

vorgelagerten WebDispatcher<br />

einem anderen NWAS des<br />

Systems zugeteilt werden !<br />

Web<br />

Disp.<br />

NWAS 1<br />

NWAS 2<br />

NWAS 3<br />

Vorteil ist die Ressourcennutzung: Auf dem NWAS werden nur während der Bearbeitung eines<br />

Requests Ressourcen beansprucht. Sofortiges Verwerfen des Applikationskontext nach Absenden der<br />

Response vermeidet unnötige Speicherbelegung durch zahlreiche "flüchtige" Internetuser. Stateless<br />

Programmierung führt somit zu einer guten Skalierung des Servers und ist für Web-Anwendungen<br />

(viele "flüchtige" Anwender) gut geeignet.<br />

Nachteilig ist die hohe Zahl von Datenbankzugriffen. Daten müssen ständig neu beschafft werden:<br />

Daten, die mehrere BSPs innerhalb einer BSP-Anwendung benötigen, müssen mehrfach von der DB<br />

gelesen werden. Dies führt zu einer höheren Last auf der DB und zu Laufzeitverlängerungen<br />

gegenüber dem Stateful-Zustandsmodell. Es gibtLösungen diesen Nachteil zu umgehen (s.u.)<br />

Für Applikationen, die keine Statusdaten brauchen, ist stateless (Default) sinnvoller, da nicht unnötig<br />

Ressourcen im SAP-System belegt werden.<br />

In den Eigenschaften einer BSP-Anwendung läßt sich festlegen, ob die gesamte Anwendung stateful<br />

oder stateless gehandhabt wird. Aber es ist auch möglich, zwischen stateful und stateless zur<br />

Laufzeit umzuschalten (gemischte BSP-Applikation). Dies wird zur LZ erreicht durch das Setzen<br />

des Attributs keep_context des Objekts runtime (Typ if_bsp_runtime). Das Umschalten ist in den<br />

Eventhandlern möglich. Typischerweise würde die Anwendung stateful geschaltet, wenn der<br />

Anwender Daten eingibt, die im weiteren Verlauf der Anwendung weiterhin benötigt werden.<br />

Da stateless BSP-Anwedungen keinen Kontext auf dem NWAS für den User speichern, kann jeder<br />

Teilschritt einer Webanwendung von einem anderen NWAS eines verteilten SAP-Systems bearbeitet<br />

werden – im Gegensatz zu einer klassischen SAP-Anwendung, bei der der User nach seiner<br />

Anmeldung (via Message Server) einem bestimmten NWAS fest zugeteilt wird und im Rollbereich<br />

dieses NWAS ein entsprechender Userkontext gehalten wird. In einer stateless BSP-Anwendung wird<br />

somit Session-Stickness vermieden und die Skalierbarkeit des Systems deutlich verbessert !<br />

Dagegen erzwingen statefull BSP-Anwendungen Session-Stickness durch das Halten von User-<br />

Kontext auf einem der NWAS. Aufeinander folgende Request der gleichen Session können somit nicht<br />

auf verschiedene NWAS verteilt werden, da der Userkontext nur auf einem der NWAS gehalten wird.<br />

Verteiltes System


Weitergabe von Seitenattribut-Werten innerhalb BSP<br />

1<br />

2<br />

Browser<br />

Wertübergabe bei impliziter + expliziter Navigation :<br />

OnInputProcessing<br />

Navigation<br />

OnInitialization<br />

On<br />

Initialization<br />

req.<br />

Seitenattribut :<br />

test type string<br />

Layout<br />

Request-Response-Zyklus<br />

= Kontextlebensdauer<br />

res.<br />

BSP 1<br />

Browser<br />

req.<br />

Formular<br />

On<br />

InputProcessing<br />

.....<br />

.....<br />

Bereits behandelt ....<br />

(5)<br />

Identisch für stateless und stateful<br />

Unterschiedlich für AUTO und<br />

Nicht-AUTO-Seitenattribute<br />

Seitenattribut test füllbar in :<br />

1. OnInitialization<br />

2. Layout<br />

3. Form<br />

Wert in OnInputProcessing<br />

noch gefüllt und abgreifbar?<br />

Identisch für AUTO und Nicht-<br />

AUTO-Seitenattribute<br />

Unterschiedlich für stateless<br />

und stateful Anwendungen<br />

................................<br />

Bereits behandelt wurde die Übergabe von Seitenattribut-Werten aus dem Zeitpunkt<br />

OnInputProcessing an OnInitialization. Hier wurden verschiedene Arten der Navigation auf die gleiche<br />

oder eine andere BSP unterschieden. Es ging um die Wertübergabe:<br />

OnInputProcessing → ( Navigation ) → OnInitialization<br />

Das Verhalten hing nicht davon ab, ob die BSP-Anwendung stateful oder stateless lief. Dagegen<br />

war entscheidend, ob es sich um ein Auto-Seitenattribut oder Nicht-Auto-Seitenattribut handelte.<br />

Nun betrachten wir die Übergabe von Seitenattribut-Werten aus den Zeitpunkt OnInitialization,<br />

Layout und Formbearbeitung an OnInputProcessing. Es geht um die Wertübergabe:<br />

OnInitialization / Layout / Formular → OnInputProcessing<br />

Das Verhalten hängt nicht davon ab, ob es sich um ein Auto-Seitenattribut oder Nicht-Auto-<br />

Seitenattribut handelt. Dagegen ist es entscheidend, ob die BSP-Anwendung stateful oder stateless<br />

läuft.


Weitergabe von Seitenattribut-Werten innerhalb BSP<br />

Seitenattribut test gefüllt in: Zugriff in:<br />

1. OnInitialization<br />

2. Layout<br />

OnInputProcessing<br />

3. Form<br />

Noch gefüllt + abgreifbar in OnInputProcessing :<br />

Seitenattribut stateless stateful<br />

gefüllt in :<br />

OnInitialization nein ja<br />

Layout nein ja<br />

Form (Benutzereingabe) ja request→get_form_field( ) ja<br />

(6)<br />

Identisch für AUTO und Nicht-<br />

AUTO-Seitenattribute<br />

Unterschiedlich für stateless<br />

und stateful Anwendungen<br />

Man bewegt sich in ein und derselben BSP-Seite<br />

Dennoch Seitenattribut-Werte nicht ständig verfügbar !<br />

BSP-Seitengrenzen nicht identisch mit Umfang eines Request-Response-Zyklus !<br />

In der Tabelle ist das Verhalten einer stateful- und stateless-BSP-Anwendung gegenübergestellt.<br />

Wenngleich man sich ständig innerhalb einer BSP-Seite bewegt, stehen die einmal gefüllten<br />

Seitenattribut-Werte nicht ständig zur Verfügung - was eventuell der Intuition widerspricht.


Halten von Kontextdaten in stateless BSP- Anwendungen :<br />

Hidden Fields + Clientseitige Cookies + Serverseitige Cookies<br />

HTTP ist stateless, aber :<br />

Betriebswirtschaftliche Transaktionen benötigen internen Zustand = Userkontext<br />

Dieser ist der Inhalt der laufenden Session und einem Client zugeordnet<br />

Abhilfe :<br />

Zustand als Parameter versteckt / gespeichert, den Serverprogramm auswertet :<br />

→ Korrekte Fortsetzung der Transaktion<br />

→ Generieren der korrekten Response<br />

1. Unsichtbare Eingabeelemente <br />

2. Clientseitige Cookies<br />

3. Serverseitige SAP-"Cookies" auf DB<br />

Anwendungsfall :<br />

Weitergabe von User- bzw. Rollen-<br />

Informationen für angemeldete User<br />

Ein gutes Beispiel für die Notwendigkeit, den Benutzerkontext (Zustand) aufzubewahren ist der<br />

Warenkorb eines OnlineShops:<br />

Die Auswahl des Besuchers muss im Warenkorb gespeichert bleiben, selbst wenn zwischendurch<br />

die Verbindung unterbrochen wird. Wenn der Kunde, nachdem er einige Waren aufgenommen hat,<br />

eine andere Webseite aufsucht und dann zum Onlineshop zurückkehrt, sollen alle Inhalte und<br />

Details des Warenkorbes noch vorhanden sein. Dazu müssen diese Informationen als Kontext<br />

gespeichert werden.<br />

Die Gedächtnisfreiheit von HTTP (und somit das wiederholte Einlesen von Daten) läßt sich auch in<br />

einer stateless BSP-Anwendung durch verschiedene Techniken umgehen:<br />

a) Unsichtbares Eingabeelement: Ablegen von Daten in einem HTML-Formular durch hidden<br />

fields. Diese werden über mehrere Seiten hinweg innerhalb von Forms übertragen.<br />

b) Cookies (client- und serverseitig): Das ICF bietet Methoden zum Senden und Empfangen von<br />

Cookies. Die Lebensdauer von Cookies kann bestimmt werden.<br />

(7)


Hidden Fields<br />

Für Benutzer unsichtbares Weiterreichen von<br />

Formulardaten = Name-Wert-Paare über mehrere Seiten<br />

Behandlung analog zu sichtbaren Formularfeldern<br />

In BSP mit navigation→set_parameter() an Folgeseite<br />

CASE event_id. OnInputProcessing<br />

WHEN 'Opt'.<br />

navigation→set_parameter( 'user' ).<br />

navigation→goto_page( 'next.htm' ).<br />

ENDCASE.<br />

first.htm<br />

<br />

Layout<br />

<br />

<br />

<br />

<br />

Form<br />

13<br />

Form<br />

13<br />

Form<br />

Hidden field<br />

13<br />

13<br />

(8)<br />

Web<br />

Server<br />

next.htm<br />

Seitenattribut Auto<br />

user X<br />

..............................<br />

In einem HTML-Formular sind für den Anwender nicht sichtbare, versteckte HTML-Input-Felder vom<br />

Typ hidden mit zugewiesenen Werten abgelegt. Nach Versand des Formulars mittels Submit kann<br />

das versteckte Textfeld auf dem Server ausgelesen und die Information weiterverarbeitet werden.<br />

Der Inhalt versteckter Textfelder (hidden fields) wird innerhalb eines HTML-Formulars versteckt<br />

mitgesendet. Diese Informationen sind - analog zu sichtbaren INPUT-Feldern - in Name-Wert-Paaren<br />

zusammengefasst. Man nutzt dieses Verfahren, um Variablen, die auf mehreren oder allen Seiten der<br />

Anwendung benötigt werden, „durchzuschleifen“, dh von Seite zu Seite zu übergeben.<br />

Für eine BSP ist es irrelevant, ob Werte aus einer Form als Typ = "hidden" oder Typ = "text" beim<br />

Request mitgesendet werden. Damit die eingegebenen Daten auch in der Folgeseite verarbeitet<br />

werden können, muss in allen Fällen mit navigation→set_parameter() der Wert weitergeleitet werden.<br />

Die Folgeseite muss ein gleichnamiges Auto-Seitenattribut besitzen, um den Wert<br />

entgegennehmen zu können.<br />

Mit der Technik der versteckten Felder lassen sich somit Informationen über mehrere Seiten in<br />

HTML-Formularen übertragen. Dazu muss das Name-Wert-Paar auch in den Folgeseiten wiederum<br />

als hidden field an deren Folgeseiten übertragen werden. Eine typische Anwendung ist die<br />

Weitergabe von User- bzw. Rolleninformationen innerhalb einer stateless BSP-Anwendung. Dies<br />

kann alternativ natürlich auch mit Cookies geschehen.<br />

Es ist nicht möglich, die Werte interner Tabellen auf diese Weise zu übertragen!<br />

Zum Weiterreichen derselben hidden fields über mehrere Seiten einer BSP-Anwendung sind<br />

Seitenfragmente gut geeignet.<br />

Nachteile: Es ist umständlich, die hidden fields in allen relevanten Seiten zu deklarieren. Ferner gibt<br />

es browserabhängige Größenbegrenzungen für Formularfelder. Somit eignen sich hidden fields nur für<br />

kleine Steuerungs-Kontexte.


Client- Cookies<br />

HTTP-Client (Browser)<br />

Request<br />

Response<br />

Request<br />

HTTP-Server<br />

(9)<br />

HTTP- Cookie :<br />

Bis zu 4 KB pro Cookie, nur Strings<br />

Maximal 300 pro Client<br />

Maximal 20 pro Server oder Domäne<br />

Von Server mit HTTP-Response gesendet<br />

Vom Client mit nächsten Request automatisch<br />

zur selben Server-URL zurückgeschickt.<br />

Lebendauer :<br />

- temporär - persisitent (mit Verfallsdatum)<br />

Data: booklist type string.<br />

OnInputProcessing BSP-Objekte und<br />

request→get_cookie(<br />

Methoden zum Aus-<br />

exporting name = 'basket'<br />

lesen und Übergeben<br />

importing value = booklist ).<br />

von Cookies an HTTP-<br />

* Cookie-Datenstring booklist in interne Tabelle überführen<br />

Strom ......<br />

* Interne Tabelle = Einkaufskorb verwalten<br />

Keine internen Tabellen -<br />

* Interne Tabelle wieder in Cookie-Datenstring booklist überführen<br />

müssten erst serialisiert<br />

* .......................................................<br />

werden ............<br />

response→set_cookie(<br />

SS0-Tickets + SessionIDs<br />

exporting name = 'basket'<br />

werden als nichtpersistente<br />

Cookies geführt ⇒<br />

value = booklist<br />

Lebensdauer + Gültigkeit für<br />

expires = 'TUE, 30-MARCH-13 23:59:59 GMT ' ).<br />

eine Session<br />

SSO-Cookie = MYSAPSSO2<br />

Eigenschaften eines HTTP-Client-Cookies: Bis zu 4 KByte Textdaten werden vom Server in der<br />

angeforderten HTTP-Response eingebettet und mitgesendet. Den Inhalt des Cookies legt der Server<br />

fest. Cookies können serverseitig gesetzt und bereits im HTTP-Protokollkopf mitgeschickt werden -<br />

oder mittels des JS-Objekts document.cookie clientseitig erst beim Aufbau der HTML-Seite im<br />

Browser gesetzt werden. Die Wirkung ist dieselbe: Bei jedem folgenden Request werden die Cookie-<br />

Daten im HTTP-Protokollkopf zurück an den Server gesendet. Das Cookie enthält :<br />

1. Die zu sichernde Information - benutzerbezogene Zustandsinformationen, z.B. fortlaufende<br />

Nummern für den Zustand einer Transaktion, oder der aktuelle Inhalt eines Warenkorbs. (Name-<br />

Wert-Paare). 2. Die Gültigkeitsdauer. 3. Die URL des Ursprungsservers (Domain).<br />

Nachteil von Client-Cookies: Da sie Teil des HTTP-Protokolls sind, können sie nur Felder vom Typ<br />

String enthalten – keine internen Tabellen. Da sie bei jedem Request mitgesendet werden erzeugen<br />

Cookies auch Netzlast.<br />

Ein Cookie kann von einem Server nicht aktiv angefordert werden. Der Server sendet Cookies zum<br />

Browser. Der Client-Browser schickt den Inhalt des Cookies beim nächsten HTTP-Request zum<br />

gleichen Server automatisch wieder mit.<br />

Die Lebensdauer ist entweder a) temporär - der Browser hält den Cookie nur im Speicher, mit Ende<br />

der Browser-Session geht der Cookie verloren. Oder b) persistent - der Browser speichert das Cookie<br />

lokal in einem festgelegten Verzeichnis, so dass es in der nächsten Browser-Session wieder<br />

ausgelesen werden kann. Der Server gibt dazu dem Cookie ein Verfallsdatum (expiration date) mit,<br />

nach dessen Ablauf der Browser den Cookie nicht mehr sendet. Browsereinstellungen legen fest,<br />

ob Cookies überhaupt oder erst nach Nachfrage angenommen werden sollen. Sie lassen sich durch<br />

den Browseruser abschalten. Somit sind clientseitige Cookies kein sicheres Mittel. Clienseitige<br />

Cookies sollten also nicht als "Basisfunktionalität" einer Anwendung eingesetzt werden, sondern<br />

allenfalls als Ergänzung. Sicherlich ist es nicht sinnvoll, sensible Daten als Browser-Cookie abzulegen.


Client- Cookies<br />

Zahlreiche(!) weitere Methoden zur Cookie-Manipulation<br />

im Interface IF_HTTP_ENTITY<br />

Importparameter von set_cookie()<br />

Name Typ Bedeutung<br />

name String Name des Cookies<br />

value String Wert des Cookies<br />

domain String Domäne der aufgerufenen Seite.<br />

Wenn nicht gesetzt, dann Hostname des sendenden Servers<br />

path String Gültiges Verzeichnis auf Server; Default = Pfad der aktuellen Seite<br />

expires String Zeitstempel, legt Ende der Lebensdauer fest.<br />

Format: Wdy, DD-Mon-YY HH:MM:SS GMT<br />

zB Tue, 30-March-12 23:59:59 GMT<br />

secure i Wert : 1 Cookie nur über HTTPS-Verbindung sendbar<br />

Wert : 0 Cookie auch über ungesicherte Verbindung sendbar<br />

Mögliche Aufrufzeitpunkte von :<br />

get_cookie: OnRequest, OnInitialization, OnInputProcessing, OnManipulation<br />

set_cookie: OnInitialization, OnManipulation<br />

Das ICF bietet in IF_HTTP_ENTITY Methoden zum Versenden und Empfangen clientseitiger<br />

Cookies. Diese Methoden stehen über die automatisch instantiierten Objekte request und response<br />

zur Vfg. Man kann einen beliebigen String im Cookie ablegen. Die Lebensdauer des Cookies ist<br />

durch einen Zeitstempel steuerbar:<br />

Wenn man kein Verfallsdatum angibt, wird das Cookie beim Schließen des Browsers<br />

(Sessionende) gelöscht. Soll das Cookie auf dem Client abgelegt werden, dann muss dem Cookie<br />

in der Methode set_cookie() ein Verfallsdatum mitgegeben werden.<br />

Cookies mit abgelaufenem Verfallsdatum werden nicht mehr an den Server gesendet. Vom WAS<br />

aus können clientseitige Cookies gelöscht werden. Dazu sendet man das Cookie erneut an den<br />

Client, datiert das Verfallsdatum jedoch in die Vergangenheit.<br />

Das Pfad-Attribut beschränkt das Cookie auf bestimmte Server-URLs. Der Wert '/' drückt aus, das<br />

das Cookie für alle URLs auf dem Server gültig ist. Weitere mögliche Werte sind z.B. runtime-<br />

>runtime_url oder runtime->page_url.<br />

Die Klasse CL_BSP_UTILITY verfügt über die statsiche Methode data_to_string_http, mit der sich ein<br />

Zeitstempel im gewünschten Format erzeugen lässt:<br />

Data: ts TYPE bsptimestamp, tss(14) TYPE c.<br />

ts-date = sy-datum . ts-time = sy-uzeit . tss = ts .<br />

Cl_BSP_UTILITY⇒ date_to_string_http( timestamp = tss ) .<br />

Die Objekte request und response stehen mit ihren Methoden nur zu den angegebenen Zeitpunkten<br />

zur Vfg. Zu anderen Zeitpunkten kann man sich die aktuellen Instanz-Referenzen über die Referenz<br />

page der aktuellen BSP-Seitenklasse beschaffen:<br />

Data: response TYPE REF TO if_http_resonse , request TYPE REF TO if_http_request .<br />

response = page→get_response( ) . request = page→get_request( ) .<br />

(10)


Cookies auf Server : Serverseitige SAP-Cookies<br />

HTTP-Client (Browser)<br />

Request<br />

Response<br />

Request<br />

WAS<br />

Server-Cookie :<br />

- Speichern von Zustandsdaten, Kontext in DB-Tabelle sscookie<br />

- Beliebige Typen und Mengen → Einzelwerte, Strukturen, ganze Tabellen<br />

- Lebensdauerkonzept<br />

- Zugriff durch statische Methoden der Klasse CL_BSP_SERVER_SIDE_COOKIE<br />

Vorteil : Ergebnisse komplexer Selects für direkten Wiederzugriff abgelegt<br />

Standardprogramme :<br />

BSP_SHOW_SERVER_COOKIES : zeigt alle Server-Cookies an<br />

BSP_CLEAN_UP_SERVER_COOKIES : löscht alle abgelaufenen Server-Cookies<br />

DB<br />

Serverseitiges<br />

Cookie :<br />

Speichern von<br />

Kontext in<br />

stateless-<br />

Anwendungen<br />

Serverseitigen SAP-Cookies: Serverseitige Cookies sind persistente Daten (Felder, Strukturen,<br />

ganze Tabellen) und werden in der DB des Systems abgelegt. Somit ist das Setzen und Lesen eines<br />

Server-Cookies ein DB-Zugriff. Sie werden nicht zwischen Client und Server hin und her transferiert.<br />

Es existiert ein Verfallszeitmechanismus.<br />

Ihr Vorteil:<br />

Man kann beliebig viele anlegen und sie haben unbegrenzte Größe.<br />

Anwendungsdaten werden in oft komplexen select-Anweisungen aus mehreren DB-Tabellen<br />

zusammengestellt. Indem man die einmal in einer internen Tabelle gesammelten Daten in Form<br />

eines serverseitigen Cookies auf der DB ablegt .....<br />

vermeidet man wiederholte komplexe Select-Anweisungen, und greift stattdessen die<br />

gewünschten Daten in einem einfacheren Zugriff ab.<br />

beschränkt man den DB-Zugriff auf die gewünschte Menge.<br />

Somit kann der Einsatz von Server-Cookies die Performanz steigern.<br />

Die Klasse CL_BSP_SERVER_SIDE_COOKIE stellt Methoden zum Arbeiten mit serverseitigen<br />

Cookies zur Vfg. Folgende Standardprogramme sind hilfreich:<br />

BSP_SHOW_SERVER_COOKIES : zeigt alle Server-Cookies an<br />

BSP_CLEAN_UP_SERVER_COOKIES : löscht alle abgelaufenen Server-Cookies<br />

Sollte als periodischer Batch-Job vom WAS-Admin eingeplant werden, da Server-Cookies, deren<br />

Verfallszeit abgelaufen ist, nicht automatisch gelöscht werden. Das Programm besteht nur aus<br />

einer Zeile: delete from sscookie where expiryd lt sy-datum.<br />

Jedoch wird beim Auslesen ein serverseitiges Cookie automatisch aus DB gelöscht. Soll es erhalten<br />

bleiben, muss es sofort im Anschluss an den Lesevorgang wieder auf die DB geschrieben werden. Die<br />

Verfallszeit kann in Sekunden oder Tagen angegeben werden.<br />

(11)


SAP-Server-Cookies : CL_BSP_SERVER_SIDE_COOKIE<br />

* Server-Cookie Schreiben :<br />

Cl_BSP_SERVER_SIDE_COOKIE ⇒set_server_cookie (<br />

exporting<br />

name = 'booklist'<br />

application_namespace = runtime→application_namespace<br />

application_name = runtime →application_name<br />

username = sy-uname<br />

session_id = runtime→session_id<br />

data_name = 'it_book_data'<br />

data_value = it_book_data<br />

expiry_time_rel = 3600 ).<br />

Server-Cookie auch unabhängig von bestimmter Anwendung,<br />

Session, User speicherbar<br />

In diesem Fall: application_namespace = 'NONE'<br />

application_name = 'NONE'<br />

username = 'NONE'<br />

session_id = 'NONE'<br />

(12)<br />

Server-Cookie hier<br />

angelegt für :<br />

- aktuellen User,<br />

- aktuelle Anwendung<br />

- aktuelle Session<br />

Nur für diese später<br />

auch zugreifbar ....<br />

Anwendungsname und<br />

Session-ID als Attribute des<br />

globalem Objekt runtime in<br />

allen Eventhandlern und<br />

Layout zur Vfg.<br />

Die Methodenparameter von set_server_cookie() und ihre Bedeutung:<br />

name Beliebiger Name des Cookies als String<br />

username Beleibiger Benutzername als String, muss nicht mit sy-uname<br />

übereinstimmen<br />

data_name Datenname als String – muss identisch mit 'data_value' sein<br />

data_value Dateninhalte beliebigen Typs, auch interne Tabellen<br />

expiry_time_rel Relative Lebensdauer in Sekunden als Integer<br />

expiry_date_rel Relative Lebensdauer in Tagen als Integer<br />

expiry_time_abs Absolute Lebensdauer als Zeitfeld vom Typ T<br />

expiry_date_abs Absolute Lebensdauer als Datumsfeld vom Typ D<br />

Mit den Parametern data_value und data_name werden zu sichernder Datenobjektinhalt und<br />

Datenobjektname festgelegt. Dies ist notwending, wel die Cookie-Daten in einer generischen<br />

Clustertabelle auf der DB gespeichert werden.<br />

Zur endeutigen Identifizierung eines Server-Cookies sind die Session-ID sowie Benutzer- und<br />

Anwendungsname geeignet. Diese Werte stehen als Attribute des automatisch instantiierten Objekts<br />

runtime zur Vfg.<br />

Man kann ein Server-Cookie auch Anwendungs-, Session- und Benutzer-unabhängig ablegen! In<br />

diesem Fall würde man den Parametern:<br />

application_namespace application_name username session_id<br />

einfach zB als Default den String-Wert 'NONE' zuweisen.<br />

Auf Server-Cookies deren Vefallszeit abgelaufen ist, kann nicht mehr zugegriffen werden. Sie werden<br />

jedoch nicht automatisch aus der DB gelöscht.


SAP-Server-Cookies : CL_BSP_SERVER_SIDE_COOKIE<br />

* Server-Cookie Lesen<br />

Data: ex_d type SYDATE .<br />

Data: ex_t type SYTIME .<br />

Cl_BSP_SERVER_SIDE_COOKIE ⇒get_server_cookie (<br />

exporting<br />

name = 'booklist'<br />

application_namespace = runtime→application_namespace<br />

application_name = runtime →application_name<br />

username = sy-uname<br />

session_id = runtime→session_id<br />

data_name<br />

importing<br />

= 'bookshop_data'<br />

expiry_date = ex_d<br />

expiry_time<br />

changing<br />

= ex_t<br />

data_value = it_book_data ) .<br />

* Server-Cookie Löschen - unahängig von Lebensdauer<br />

Cl_BSP_SERVER_SIDE_COOKIE ⇒ delete_server_cookie ( .... ) .<br />

(13)<br />

Alle Parameter außer<br />

Lebensdauer sind auch<br />

beim Auslesen<br />

mitzugeben ...<br />

Alle Parameter (außer der Lebensdauer) müssen auch beim Lesen wieder mitgegeben werden, damit<br />

das richtige Cookie gefunden wird.<br />

Die Methode get_server_cookie() liefert auserdem die Lebensdauer des Server-Cookies zurück,<br />

und zwar als Datums- oder Zeitfeld in Form der SAP-Dictionary-Datentypen SYDATE und SYTIME.<br />

Es existiert eine eigene Methode delete_server_cookie(), um das Cookie manuell (unabhängig von<br />

seiner Lebensdauer) aus der DB zu löschen:<br />

Cl_BSP_SERVER_SIDE_COOKIE ⇒ delete_server_cookie (<br />

exporting<br />

name = 'booklist"<br />

application_namespace = runtime→application_namespace<br />

application_name = runtime →application_name<br />

username = sy-uname<br />

session_id = runtime→session_id ).<br />

Anmerkung: Der Hauptspeicher des NWAS scheidet zum Zwischenspeichern von Sitzungs-daten<br />

aus, da die Session bei jedem Request auf einer anderen Maschine bearbeitet werden kann, falls es<br />

sich um ein SAP-System aus mehreren WAS handelt. Die Verteilung des Speicherinhalts auf alle<br />

Applikationsserver wäre zu aufwendig.


Datenhaltung bei Stateless-Anwendungen<br />

via Netzwerk<br />

Hidden Field, set_parameter()<br />

- Begrenzte Datenmenge<br />

- Benötigt keine DB-Zugriffe<br />

- Belastet das Netzwerk<br />

Client-Cookie<br />

- Begrenzte Zahl und Datenmenge<br />

- Vom Browser-User abschaltbar<br />

- Benötigt keine DB-Zugriffe<br />

- Belastet das Netzwerk<br />

via Datenbank<br />

Quelle: [WOLF03]<br />

Server-Cookie + DB-Ablage<br />

- Unbegrenzte Datenmenge<br />

- Schont das Netzwerk<br />

- Belastet die DB-Performanz<br />

- Belegt DB-Speicher<br />

- Regelmäßiges "Säubern" nötig<br />

Stateful oder Stateless :<br />

Sehr grosse Zahl gleichzeitiger User ⇒ stateless<br />

Komplexere Anwendung, wenige User, aufwendige DB-Zugriffe ⇒ stateful<br />

Auch - oder gerade - in stateless Applikationen stellt sich die Frage, wie trotz Verlustes des<br />

Anwendungskontextes nach jedem Request, z.B. Benutzereingaben über mehrere Requests hinweg<br />

gerettet werden können. Hierzu ist entsprechende Funktionalität zu programmieren. Es gibt zwei<br />

Verfahrensgruppen - Verwendung des Netzwerks bzw der Datenbank.<br />

Je kleiner die zu haltende Datenmenge (bis zu wenigen kB), desto effizienter ist die Netzwerk-<br />

Lösung. Wird die bei jeder Benutzeraktion über das Netz zu übertragende Datenmenge größer,<br />

resultieren jedoch schnell nicht mehr akzeptable Antwortzeiten.<br />

Die Datenbank hat den Vorteil, dass in ihr auch grosse Datenmengen verarbeitet werden können.<br />

Allerdings ist die Datenbank der "bottleneck" des SAP-Systems.<br />

Daumenregel: stateful oder stateless?<br />

Als Daumenregel gilt, daß Internetszenarien, die von einer potenziell sehr großen Anzahl Benutzer<br />

gleichzeitig genutzt werden, eher stateless arbeiten sollten. Komplexere Anwendungen, die von<br />

einer begrenzten Anzahl Benutzer parallel eingesetzt werden und die über einem teurer zu<br />

ermittelnden Datenbestand operieren, bieten sich eher für das stateful Programmieren an.<br />

Stateless BSP-Applikation blockieren nur dann Server-Ressourcen, solange ein einzelner Request<br />

bearbeitet wird. Nach Abarbeitung des Requests werden alle Ressourcen an das System<br />

zurückgegeben und für andere Requests eingesetzt.<br />

Stateless Applikation erlauben somit – aus Sicht der Ressource "Speicher" – eine optimale<br />

Skalierung. Andererseits kann das Freigeben des Anwendungskontext nach jedem Request<br />

erfordern, dieselben Daten mehrfach aus der Datenbank zu lesen und aufzubereiten. Somit wird die<br />

Einsparung an Speicher durch Laufzeit-Einbußen möglicherweise kompensiert. Dies ist von Fall zu<br />

Fall genau zu analysieren und zu beurteilen.<br />

(14)


Zustände BSP-Anwendung<br />

Webbrowser 1<br />

Webbrowser 2<br />

Internet<br />

WAS<br />

1<br />

1<br />

1<br />

1<br />

2<br />

2<br />

2<br />

2<br />

Zeit<br />

(15)<br />

stateful stateless<br />

Quelle:<br />

Heinemann, Rau 2005<br />

Stateful :<br />

Seiten-Objekt bleibt<br />

erhalten ⇒<br />

OnCreate nur einmal<br />

durchlaufen !<br />

OnDestroy gar nicht !<br />

Die Aufrufabfolge der Event-Handler ist abhängig vom Zustandmodell der BSP-Applikation bzw. -<br />

Seite , da sich der Aufruf der Events nach der Lebenszeit des BSP-Seitenobjekts richtet.<br />

Stateless-Modell: Bei der Bearbeitung eines Request überprüft die BSP-Laufzeit, ob für die<br />

angeforderte Seite bereits ein Seitenobjekt existiert. Falls kein Seitenobjekt existiert, wird der<br />

OnCreate-Handler aufgerufen und durchlaufen und für die Seite ein Seitenobjekt erzeugt. Auto-<br />

Seitenattribute werden übernommen, so dass sie der Seite zur Vfg stehen. Es folgt OnRequest.<br />

Falls eine Benutzeraktion voranging, wird OnInputProcessing angestoßen. Dessen Programmcode<br />

kann auf eine andere Seite navigieren oder auf der Seite verbleiben.<br />

Im ersten Fall wird der OnDestroy-Handler aufgerufen und das Seitenobjekt zerstört. Daraufhin<br />

beginnt der Zyklus für die annavigierte Seite von Neuem.<br />

Im zweiten Fall würde OnInitialization derselben Seite aufgerufen. Im Anschluss erfolgt das<br />

Zusammenführen statischen Layout-HTMLs und dynamischen ABAP-Layout-Codes und die<br />

Einbettung in einen HTTP-Datenstrom im OnLayout-Handler. Danach wird OnManipulation<br />

aufgerufen; hierin kann der Datenstrom noch verändert werden. Daraufhin wird OnDestroy<br />

angestoßen und das Seitenobjekt gelöscht. In OnDestroy bietet es sich im stateless-Fall an,<br />

Zustandsdaten zu sichern. Abschließend wird der HTTP-Datenstrom an den Client geschickt.<br />

Das Statefull-Modell unterscheidet sich im Ablauf der Handler vom Stateless-Modell nur bezüglich<br />

des OnCreate- und OnDestroy-Events. Im Statefull-Modell bleiben die Seitenobjekte einer BSP-<br />

Applikation für die gesamte Dauer der BSP-Session erhalten.<br />

Daher wird OnCreate für jede BSP-Seite nur einmal ausgeführt.<br />

Das Event OnDestroy wird im Stateful-Fall überhaupt nicht prozessiert, da während einer Stateful-<br />

Session keine Seitenobjekte zerstört werden.<br />

1<br />

2<br />

2<br />

1<br />

2


Online Text Repository OTR : Internationalisierung<br />

Ziel: Anwendung in mehreren Sprachen (sprachunabhängig)<br />

OTR: Zentrale paketspezifische Textablage + Dienste zur Texterfassung und Übersetzung<br />

OTR-Tags im BSP-Layout verwendbar – Textarten :<br />

1. Kurztexte (Aliastexte): Unter Aliasnamen paketweit ansprechbar<br />

2. Langtexte: Direkt in OTR-Tags eingeschlossen<br />

<br />

<br />

<br />

OTR-Texte <br />

<br />

<br />

<br />

Dieser Text ist lang und kommt nur einmal vor.<br />

Deshalb im OTR als Langtext abgelegt<br />

<br />

<br />

<br />

<br />

<br />

<br />

Paketname immer Teil des<br />

Aliasnamens !<br />

(16)<br />

<br />

..... <br />

Alle BSP-Seiten und Seitenfragmente können auf<br />

alle OTR-Texte des Pakets zugreifen ......<br />

Pflege der OTR-Texte :<br />

a) Kurztexte aus Layout durch Doppelklick auf<br />

OTR-Referenz (Vorwärtsnavigation ins Pflegebild<br />

für OTR-Texte)<br />

b) durch Menü Springen → OTR Browser<br />

c) durch Direkteinstieg in TA SOTR_EDIT<br />

Übersetzung der OTR-Texte :<br />

Springen → Übersetzung →<br />

OTR Kurztexte / Langtexte<br />

Weitere Infos in [WOLF03]<br />

Das OTR ist die zentrale Textablage. Im BSP-Layout können OTR-Direktiven eingesetzt werden.<br />

OTR-Objekte sind einem Paket zugeordnet und werden in Transportaufträge aufgenommen. Damit<br />

eine BSP-Anwendung in mehreren Sprachen zur Vfg steht, sollte man im Layout alle<br />

übersetzungsrelevanten Teile als OTR-Texte angeben. (Übersetzung in TA SE63).<br />

Es werden Langtexte und Kurztexte unterschieden, wobei die absolute Länge des abgelegten Textes<br />

nicht wesentliches Untescheidungsmerkmal ist. Wichtiger ist die Häufigkeit des Vorkommens:<br />

Kommt ein Text nur einmal in einer BSP-Anwendung vor, dann wird der Text als Langtext im OTR<br />

abgelegt. Man legt einen Langtext an, indem man im Layout der BSP den Text selbst zwischen die<br />

Tags ..... schreibt und die Seite aktiviert. Dadurch wird der Text automatisch ins<br />

OTR eingetragen. Langtexte stehen für sich selbst, dh sind nicht über einen Aliasnamen oder<br />

Schlüssel erreichbar.<br />

Wird dagegen ein Text häufig gebraucht, wird der Text unter einem Aliasnamen als Kurztext im<br />

OTR abgelegt. Über diesen Aliasnamen ist dieser Text paketweit ansprechbar und somit<br />

wiederverwendbar. Die Übersetzung muss nur einmal erfolgen. Das Anlegen von Kurztexten erfolgt<br />

durch Schreiben des Tags und Doppelklick auf den Aliasnamen. Auf<br />

einem Bildschirmbild kann man den Aliasnamen, die maximale Textlänge und den Text pflegen. Der<br />

Aliasname beginnt immer mit dem Namen des zugeordneten Pakets.<br />

Um Texte zu übersetzen, muss durch das Menü Springen → Übersetzung → OTR Langtext in die<br />

Übersetzung verzweigt werden.<br />

Man erhält einen Überblick über alle dem Paket zugeordneten OTR-Texte im OTR-Browser,<br />

erreichbar durch das BSP-Layout-Menü Springen → OTR Browser. Im OTR wird auch ein Kontext zu<br />

jedem OTR-Objekt (Ersteller, Datum, ....) gespeichert.


OTR<br />

Notation nur im Layout-Bereich anwendbar.<br />

Jedoch auch in Eventhandlern und Anwendungsklasse Zugriff auf Kurztexte<br />

mittels runtime-Objekt oder Klasse CL_BSP_GET_TEXT_BY_ALIAS :<br />

DATA: alias TYPE STRING, text TYPE STRING.<br />

alias = ' zbsptest/gruss '.<br />

text = runtime→get_otr_text( alias ).<br />

CL_BSP_GET_TEXT_BY_ALIAS ⇒ get_text (<br />

exporting language = 'EN'<br />

alias = alias<br />

importing alias_text = text ) .<br />

OTR-Texte können in Übersetzungsvarianten<br />

unterschiedliche Länge aufweisen ⇒<br />

Abschneiden überschüssiger Leerzeichen im<br />

Layout durch BSP-Seitendirektive :<br />

<br />

Die OTR-Referenz wird intern in einen Methoden-aufruf der runtime-Instanz<br />

aufgelöst. Der Methodenaufruf cl_bsp-runtime=>get_otr_text() kann auch in den Eventhandlern und<br />

der Anwendungsklasse erfolgen - aber auch in Reports und konventionellen ABAP-Programmen<br />

außerhalb der BSP-Programmierung. Vertiefung siehe [WOLF03].<br />

Eine Anwendungsmöglichkeit ist die sprachunabhängige Hinterlegung von Fehlermeldungen, die<br />

beim Prozessieren der Eventhandler erzeugt werden - oder die Wiederverwendung von Texten auch<br />

innerhalb derselben Sprache. OTR-Texte werden auf dem WAS gepuffert - dies macht den Zugriff<br />

schnell. Durch das Systemkommando /$otr kann der OTR-Puffer manuelle zurückgesetzt<br />

werden, wenn Textänderungen vorgenommen worden sind.<br />

Auch mittels der Klasse CL_BSP_GET_TEXT_BY_ALIAS kann auf OTR-Texte zugegriffen werden.<br />

Die Klasse hat nur eine statische Methode get_text(). Hier kann auch die gewünschte Sprache als<br />

Parameter übergeben werden. Somit können auch Texte in Sprachen verwendet werden, die nicht der<br />

Anmeldesprache des Benutzers entsprechen.<br />

Damit eine BSP in der gewünschten Sprache angezeigt wird, muss diese Sprache im benutzten<br />

Service hinterlegt sein. Dazu werden in der TA SICF Alias-Services eingerichtet.<br />

OTR-Texte können Parameter beinhalten. Hierzu wird der Platzhalter für den Parameter durch das &<br />

eingeschlossen. Nur Buchstaben, Ziffern und Unterstrich sind erlaubt. Zur Laufzeit wird dann der<br />

benannte Platzhalter durch einen Wert (zB Seitenattribut) ersetzt.<br />

Wenn man einen OTR-String anlegt und in verschiedene Sprachen übersetzt, so wird die Länge der<br />

übersetzten Strings in der Regel in den verschiedenen Sprachen unterschiedlich sein. Ein langer<br />

OTR-String könnte sehr kurze Übersetzungen besitzen. Allerdings wird mit dem String in allen seinen<br />

Übersetzungen durch das OTR-System stets nur die Länge des Orginal-Strings gespeichert - und<br />

liefert somit eventuell OTR-Strings mit überschüssigen Spaces zurück, so dass HTML-Darstellungen<br />

unschön verbreitert sind. Die o.a. BSP-Seitendirektive sorgt dafür, dass aus jedem in die Seite<br />

eingefügten OTR-Strings überflüssige Spaces entfernt werden.<br />

(17)


Timeout-Situationen : Session-Timeout und Processing-Timeout<br />

1. Session-Timeout<br />

HTTP → Server kann nur passiv auf eingehende Client-Requests reagieren.<br />

⇒ Server merkt nicht, wenn Client abspringt (Browser schliesst, wegnavigiert, untätig ist)<br />

⇒ Bindet kritische Ressourcen (Status) auf dem Server<br />

⇒ Inaktive Sessions nach vorkonfigurierter idle time vom ICM abgeräumt !<br />

Rollback, Sperren(!) + Ressourcen freigeben, Session gelöscht<br />

Maximum idle time :<br />

Konfiguriert in Systemprofil-Parameter rdisp/plugin_auto_logout<br />

Default : 30 Minuten<br />

Client-Browser bekommt timeout allerdings nicht mit, nicht vom Server darüber informierbar.<br />

Erst bei nächsten Roundtrip tritt Fehler wegen inaktiver Session auf.<br />

Benutzer-Info mittels Workaround : Nach idle time → Browser-Fenster via Skript löschen:<br />

<br />

var T = 31 /*min*/ * 60 * 1000; /*ms*/<br />

window.setTimeout( "document.URL = 'about:blank' ;" , T ) ;<br />

<br />

Gilt stets für gesamten WAS<br />

und alle BSP-Anwendungen,<br />

nicht für einzelne BSP-<br />

Anwendungen verlängerbar,<br />

jedoch in SICF verkürzbar<br />

Der Session-Timeout bezieht sich auf das Browser-Verhalten: Wenn sich Browser nach Ablauf dieser<br />

Zeit nicht mehr meldet wird der Kontext verworfen<br />

HTTP ist ein striktes Request/Response-Protokoll; der Server kann nur auf einen Browser-Request hin<br />

tätig werden. Der Server kann nicht beim Browser "nachfragen": "Bist du noch da". Er kann höchstens<br />

für sich einen internen Timeout setzen und nach dessen Ablauf die Verbindung schliessen. Der Server<br />

kann dem Browser zu keinen Zeiten "zwischendurch" Informationen zukommen lassen. Somit kann<br />

der Browser auch nicht über eingetretene Session-Timeouts informiert werden. Die Session wird<br />

einfach beendet, ohne dass im Browser-Fenster etwas darauf hindeutet. Wenn der User später an<br />

seinen Arbeitsplatz zurückkehrt, so sieht er im Browser weiterhin die offene scheinbar noch laufende<br />

BSP-Anwendung. Erst bei der nächsten Interaktion tritt ein Fehler auf und der User bemerkt die<br />

verlorene Session.<br />

Als Hilfe für den User dient das clientseitige JavaScript, das in jede Seite der BSP-Anwendung<br />

einzubauen wäre. Solange der User mit der BSP-Anwendung interagiert setzt sich jede neu geladene<br />

Seite einen neu initialisiereten Timer. Im Skript gehen wir von einer Server-idle time von 30 Minuten<br />

aus. Sollte der User den Browser länger als 31 Minuten nicht bedienen (keine neuen Seiten anfordern)<br />

so wird der Brower-Screen gelöscht - und der User bemerkt auf diese Weise, dass die Session<br />

serverseitig beendet wurde.<br />

Es ist möglich, den Server darüber zu informieren, dass der Browser die BSP-Anwendung verlässt<br />

(wegnavigiert oder geschlossen wird), indem das onUnLoad-JavaScript-Event der Seiten verwendet<br />

wird und eine JavaScript-Funktion appUnload() implementiert wird. Diese wird beim Eintreten von<br />

onUnLoad augerufen. Für Details der Lösung (Verwendung von Framesets) verweisen wir auf<br />

[MCKE06], S.125ff.<br />

(18)


Timeout-Situationen : Session-Timeout und Processing-Timeout<br />

2. Processing-Timeout<br />

Maximale Prozessdauer für Bearbeitung HTTP-Request durch ABAP-Stack auf Server<br />

Bei Überschreiten wird ABAP-Session + BSP-Anwendung abgebrochen<br />

⇒ Fehlermeldung 500 Connection timed out an Browser<br />

In ICM-Verwaltung SMICM eingesehbar :<br />

Wird pro offenem HTTP-Port eingestellt<br />

Konfigurierbar in Systemprofil-Parameter: icm/server_port<br />

(19)<br />

Ermöglichen längerer Prozessdauern :<br />

Neuen HTTP- Port in SMICM<br />

definieren mit längerer maximaler<br />

Prozessdauer :<br />

Springen → Services und dort<br />

Service→ Anlegen<br />

Verfügbar nach Neustart NWAS<br />

Der "Connection Timeout" ist die Folge eines Überschreitens der maximalen Prozessdauer. Er<br />

drückt aus, dass die Verbindung zwischen ICM und ABAP-Stack eine Zeitüberschreitung erfahren hat.<br />

Die Fehlermeldung hat nichts zu tun mit einem Session-Timeout (idle timeout).<br />

Länger als dieses Zeitintervall darf die Request-Bearbeitung durch die WAS-Workprozesse nicht<br />

dauern, sonst wird die Bearbeitung abgebrochen und der User erhält eine Fehlerseite. Wenn dieses<br />

Zeit zu knapp eingestellt ist, dann kann nicht mehr richtig debuggt werden, da die Dauer des Debugs<br />

einfliesst.<br />

In Produktivsystemen ist die maximale Prozessdauer meist auf relativ kleine Werte gesetzt, so dass<br />

Workprozesse nicht lange blockiert werden können. Nachteilig kann sein, dass Debuggen von BSP-<br />

Anwendungen nicht mehr funktioniert, da der Debug-Vorgang in die Prozessdauer eingeht. Abhilfe<br />

schafft das Anlegen eines weiteren HTTP-Ports mit längerer maximaler Prozessdauer. Die URL<br />

(Port-Nummer) der BSP-Anwendung muss im Browser entsprechend geändert werden, damit sie beim<br />

Aufruf über den neuen Port läuft.<br />

Anmerkung: Die keep-alive Zeit (sichtbar in TA SMICM) legt gemäß HTTP1.1 die Zeit in Sekunden<br />

fest, die der ICM eine untätige TCP-Verbindung offen hält (und auf einen Browser-Request) wartet,<br />

ehe er sie schliesst. Die Wiederverwendung von TCP-Verbindungen verbessert das Antwortverhalten<br />

bei HTTP-Requests, da keine erneuten Roundtrips zwischen Browser und Server nötig sind, um eine<br />

neue TCP-Verbindung aufzusetzen. Allerdings ist die maximale Zahl gleichzeitig offener TCP-<br />

Verbindungen des Servers begrenzt. Deshalb werden nicht verwendete TCP-Verbindungen nach<br />

einigen Sekunden idle time geschlossen.<br />

Die Profil-Parameter des ICM können in der TA SICM eingesehen werden durch Aufruf des<br />

Memüpunkts Springen→Parameter→Anzeigen. Hier finden sich auch die Einstellungen für maximale<br />

Zeitdauern


LZ- Fehler-Behandlung<br />

Historische BSP Error Pages → seit 6.40 inaktiv !<br />

Neues Konzept ab 6.40 :<br />

(20)<br />

BSP Runtime setzt TRY / CATCH<br />

um jeden Seitenaufruf. Wenn beim<br />

Prozessieren der BSP LZ-Exception<br />

auftritt wird Kontrolle an Fehlerseite<br />

übergeben<br />

Problem :<br />

Auch die Fehlerseite ist eine BSP !<br />

⇓<br />

Nicht sinnvoll bei Schiefstand im<br />

BSP-Framework !<br />

Wenn Exception auftritt, wird BSP-Framework sofort verlassen + ICF übernimmt Kontrolle<br />

ICF-Klasse CL_HTTP_EXT_BSP fängt Fehler aus BSP-Anwendungen + erzeugt generische<br />

HTML-Fehlerseite mittels Methode REPORT_ERROR_HTML.<br />

Ausprobieren in BSP z.B. mit : <br />

Durch das Konzept der BSP Fehlerseiten konnten in jeder BSP-Anwendung eine oder mehrere<br />

BSP-Seiten als Fehlerseiten ausgewiesen werden. Dies geschah einfach durch Setzen eines Eintrags<br />

auf der Eigenschaftenseite. Problematisch daran ist, dass es sich bei der Fehlerseite selbst auch um<br />

eine BSP handelt. Tritt ein Fehler auf, so startet die Fehlerseite in der fehlerbehafteten Umgebung der<br />

Orginal-BSP. Die Fehlerseite hat keine Information darüber, was von der Orginal-BSP bereits in das<br />

HTTP response-Objekt geschrieben wurde – und somit Teil der erzeugten Antwort ist, die an den<br />

Client geht. Zudem könnte auch der gesamte BSP-Stack selbst in einem fragwürdigen Zustand sein.<br />

Schlimmstenfalls könnte die Fehlerseite selbst einen Fehler auslösen, während sie auf den Fehler der<br />

Original-BSP eingeht.<br />

Aufgrund dieser Problematik hat SAP ab NWAS 6.40 den Support für das Fehlerseiten-Konzept<br />

eingestellt: Alle Fehlerseiten-Einträge auf BSP-Eigenschaftsseiten werden seitdem einfach von der<br />

BSP-Laufzeit ignoriert. Allerdings wurde sofort ein neues Konzept ausgeliefert, das BSP-Anwender<br />

auch customizen können: Wenn eine LZ-Exception auftritt, so wird das BSP-Framework komplett<br />

verlassen und das ICF übernimmt die Kontrolle mit eingestellten Fehlerklassen. Die automatisch<br />

erzeugten generischen Fehlerseiten decken die meisten LZ-Fehler-Situationen ab und liefern viele<br />

Diagnose-Informationen.<br />

Konfigurations-Möglichkeiten: In DB-Tabelle BSPERRHANDLER kann eine andere Fehler-Klasse<br />

und ihre Methode hinterlegt werden, die im LZ-Fehlerfall gerufen wird. Mittels Wildcards (*) in der<br />

angegebenen URL kann eine Fehlerklasse für eine Gesamtheit von BSP-Anwendungen vorgesehen<br />

werden. Die Klasse CL_BSP_ERRHANDLER_SAMPLE enthält ein SAP-Beispiel für eine selbstgeschriebene<br />

Fehlerklasse und ihre Methode REPORT_ERROR_HTML. Die Klassen<br />

CL_BSP_ERRHANDLER und CL_HTTP_EXT_BSP können als Kopiervorlagen für eigene<br />

Fehlerklassen dienen.<br />

Man befindet sich dabei jedoch nicht mehr innerhalb des BSP-Frameworks. Somit können BSP<br />

Extensions und HTMLB nicht eingesetzt werden! Es ist ein HTML-String zusammenzustellen und dem<br />

ICF-response-Objekt zu übergeben: response→append_cdata( html ).


Fehler-Behandlung – Fehlermeldungen erzeugen + verwalten<br />

ICF-Klasse CL_BSP_MESSAGES erlaubt Speichern + Abfragen von Meldungen<br />

Referenz darauf durch message-Attribut des page-Objekts: page→messages<br />

Methoden zum Verwalten selbstverfasster Meldungen aufgrund eigener Prüfungen :<br />

page→messages→add_message( condition = ' feldname'<br />

message = ' Hier der erläuternde Text '<br />

severity = 1 ).<br />

if page→messages→num_messages( ) is not initial.<br />

data: feldname type string.<br />

data: text type string.<br />

data: sev type i.<br />

page→messages→get_message (<br />

exporting index = 1<br />

importing condition = feldname<br />

mesagge = text<br />

severity = sev ) .<br />

endif.<br />

page→messages→delete_message( condition = 'feldname' ).<br />

page→messages→reset( ).<br />

(21)<br />

Bedeutung der Parameter<br />

condition :<br />

Feldname, dem der Fehler<br />

zuzuordnen ist<br />

message :<br />

Beschreibender Meldungstext<br />

severity (optional) :<br />

Fehlerschwere (default = Error)<br />

Durch eigene Prüfungen innerhalb der Anwendung können Fehlersituationen unterschied-licher<br />

Schwere bemerkt werden. Die zugehörige Information kann mit einem Fehlertext, einer Fehlerstufe<br />

und der Nennung eines zugehörigen Feldnamens (dessen Werte für den Fehler verantwortlich sind)<br />

übersichtlich verwaltet werden. Dazu werden einem message-Objekt Informationen übergeben. Ein<br />

message-Objekt der Klasse CL_BSP_MESSAGES steht als Attribut messages des automatisch<br />

erzeugten page-Objekts zur Verfügung.<br />

Es ist möglich, dass mehrere Formularfelder auf einer BSP fehlerhaft sind. In diesem Fall sollte<br />

(ähnlich wie bei der Dynpro-Verarbeitung) immer nur die erste Fehlermeldung ausgegeben werden.<br />

Durch den Aufruf der Methode num_messages() kann überprüft werden, ob überhaupt ein Fehler<br />

aufgetreten ist.<br />

Eventuell ist es sinnvoll, entsprechende Methoden in der Applikationsklasse zu kapseln.<br />

Die Klasse CL_BSP_MESSAGES enthält konstante Attribute, durch die die Fehlerschwere<br />

(Fehlersufen / severity) als Integer codiert werden kann. Die Fehlerschwere ist ein Kennzeichen, durch<br />

das entschieden werden kann, ob nur eine Warnung ausgegeben werden oder aber die vielleicht die<br />

gesamte BSP-Anwendung abgebrochen werden soll:<br />

CO_SEVERITY_ERROR Fehlerstufe: Fehler<br />

CO_SEVERITY_FATAL_ERROR Fehlerstufe: Fataler Fehler<br />

CO_SEVERITY_INFO Fehlerstufe: Nur eine Information<br />

CO_SEVERITY_SUCCESS Fehlerstufe: Erfolgsmeldung<br />

CO_SEVERITY_WARNING Fehlerstufe: Nur eine Warnung


Einstellung Anmeldedialog<br />

(22)<br />

In SICF ist für einen Service das<br />

Anmeldeverhalten der Anwendung<br />

einstellbar über:<br />

Fehlerseiten • Anmeldefehler<br />

Systemanmeldung • Konfiguration<br />

Definition service-spezifischer<br />

Einstellungen möglich, die von globalen<br />

Einstellungen abweichen<br />

Mittels Aliases kann sogar für eine Anwendung eine Vielzahl unterschiedlicher Anmeldes-creens<br />

hinterlegt werden – dies ist insbesondere dann nützlich, wenn man verschiedene interne und externe<br />

Zugänge zu einer Anwendung unterstützen möchte. Insbesondere können Mandant und<br />

Anmeldesprache bereits hinterlegt werden.<br />

Das Look & Feel der Anmeldeseite kann eingestellt werden, indem man auf SAP-Vorlagen<br />

zurückgreift. Es ist jedoch sogar möglich, ein benutzerspezifisches Layout und Ablauf der Anmeldung<br />

zu hinterlegen, indem man eine ABAP-OO-Klasse hinterlegt, die von der Klasse<br />

CL_ICF_SYSTEM_LOGIN erbt. Als Kopiervorlage oder anpassbares Beispiel liefert SAP dazu die<br />

Klasse CL_ICF_EXAMPLE01_LOGIN aus.


Performance-Analyse<br />

Anteile im Zeitverbrauch für BSP-Anwendungen :<br />

1. Reine Netzwerk-Übertragungszeit: Tool ping mit Option -l für variable Datenpaketgröße<br />

C:\> ping -l 32950 m65z05.hcc.uni-magdeburg.de<br />

2. BSP-Laufzeit auf NWAS + im Netz für Request-Response-Zyklus<br />

SAP-BSP-Testprogramm IT03 → Testseiten zwischen 1 und 64 KB<br />

3. Reine Server-Prozessdauer mittels ABAP-Stopuhr-Funktion GET RUN TIME FIELD<br />

4. Browser Rendering : Seitenaubau-Zeit mittels JavaScript-Stoppuhr Date()<br />

<br />

var renderStart = new Date() ; <br />

ABAP- und Browser-Stoppuhr-<br />

....................... <br />

Funktionen klammern das<br />

Seitencoding<br />

<br />

var renderEnd = new Date() ;<br />

var renderTime = renderEnd.getTime() - renderStart.getTime();<br />

window.status = "Server=" + "" + " Render-Time=" + renderTime ;<br />

<br />

Verschiedene Anteile der Ausführungsdauer einer BSP-Anwendung sowie Analyse-Tools:<br />

Durch das ping-Tool wird die reine Netzwerk-Übertragungszeit gemessen. Das Tool sendet einen<br />

kleinen Test-Request an den Server (Datengröße einstellbar, default 32 Bytes) und der Server liefert<br />

einfach die gleichen Daten zurück. Alles passiert auf OS-Ebene und enthält kaum Server-Overhead.<br />

Die ermitelte Zeit ist die Summe aus Request + Response Zeit.<br />

Die SAP Demoanwendung IT03 liefert einfach nur Testseiten unterschiedlicher Grösse und<br />

Zusammensetzung (Text oder Bilder). Dabei sind alle Schichten des WAS beteiligt. Die benötigte Zeit<br />

ist die Zeit zum Füllen des ABAP-Buffers, übertragen zum ICM Erzeugen von HTTP, Übergabe ans<br />

Netzwerk und Übermittlung zum Browser.<br />

Die reine Server-Prozessdauer kann mittels ABAP-Stoppuhr-Funktion gemessen werden, deren<br />

paarweise Aufrufe die gesamte BSP-Seite einklammern. Das o.a. JavaScript gibt die Zeitdauer in der<br />

Statusleiste des Browsers aus. Gemessen wird die Zeit, die im Applikationscoding verbraucht wird; sie<br />

enthält keinen BSP-LZ-Overhead.<br />

Die Browser-Rendering-Zeit (Aufbau + Darstellung der HTML-Seite) muss im Browser mit JavaScript<br />

ermittelt werden. Die Differenz zwischen Start- und Endzeit der Seitenbearbeitung wird gemessen. Die<br />

JavaScript-Aufrufe stehen außerhalb des gesamten HTML-Codes der Seite.<br />

(23)


Performance-Analyse<br />

Workbench-Performance-Tools<br />

Statistical Records : ABAP-Laufzeit ohne ICM-Zeiten<br />

1. Aktivieren mit ABAP-Programm RSSTATISTIC<br />

2. BSP-Anwendungen ausführen<br />

3. TA STAD aufrufen, um die Statistik-Files auszulesen<br />

Filtern nach Ausführungszeit, User und Programm SAPMHTTP<br />

(24)<br />

Doppelklick auf Eintrag<br />

liefert detaillierte Daten<br />

zu Zeiten und<br />

Speicherverbrauch<br />

Um die aktuelle Server-Prozesszeit zu messen können Statistical-Records verwendet werden. Diese<br />

schreiben durch das ICF sehr kleine Zeitstempel in die BSP-Anwendung. Die gemessenen Zeiten<br />

geben die komplette ABAP-Laufzeit wieder. Die Statistik-Optionen sind per Default nicht für HTTP<br />

aktiviert und müssen gesondert aktiviert werden.<br />

Zum Auswerten dient die TA STAD. Der Zeitfilter ist entsprechend der Zeit des Tests zu stetzen und<br />

die Ausgabe auf das Programm SAPMHTTP zu begrenzen. (Dieses Programm ist der allererste<br />

Aktivierungsschritt, wenn HTTP-Calls auf den ABAP-Stack plaziert werden.)<br />

Typischerweise dauert der erste Seitenzugriff deutlich länger als nachfolgende Zugriffe auf die<br />

gleiche Seite. Beim ersten Zugriff muss die ABAP-Load der BSP-Seite aus der Datenbank beschafft<br />

und in den Programmpuffer geladen werden, so dass ein großer DB-Overhead resultiert, der bei<br />

folgenden Zugriffen nicht mehr auftritt.<br />

Laufzeitanalyse: Sehr detaillierte Informationen liefert die Laufzeit-Analyse, deren Daten für eine<br />

Anwendung in der TA SE30 angezeigt werden.<br />

1. Die Laufzeitanalyse für BSP-Anwendungen wird aktiviert in der Serviceverwaltung TA SICF. Dort<br />

den Menüpfad Bearbeiten→Laufzeitanalyse →Aktivieren wählen.<br />

2. Die zu analysierende BSP-Anwendung ist auszuführen.<br />

3. In der TA SE30 können die gesammelten Daten analysiert werden.<br />

Die erfassten Informationen betreffen DB-Zugriffe, Funktions- und Methoden-Aufrufe, Aufruf-<br />

Hierachien - und ermöglichen auch Sprünge an Sourcecode-Stellen, deren Laufzeitverhalten<br />

ermittelt wurde ..... und vieles mehr ....<br />

Die Besprechung dieses reichhaltigen Tools übersteigt jedoch deutlich den Fokus unserer<br />

Vorlesung. Weitere Informationen finden sich in: Thomas Schneider "SAP-Performanceoptimierung.<br />

Analyse und Tuning von SAP-Systemen."


BSP-Caching : ICM Server Cache<br />

Erhöhung Performanz + Skalierbarkeit von BSP-Anwendungen (bis zu Faktor 20)<br />

→ Für BSP-Erstellung notwendige Server-Ressourcen werden reduziert.<br />

→ Wiederholte Ausführung häufig angeforderter BSPs entfällt.<br />

Steuerung des BSP-Caching-Verhaltens<br />

1. SE80 → BSP-Eigenschaften → Caching<br />

- Verfallsdauer für Caching im Internet Server-Cache<br />

- Verfallsdauer für Caching im Browser-Client<br />

- Browserabhängiges Seiten-Caching<br />

2. Im Coding der BSP-Applikation durch Methoden von ICF-Klassen<br />

Kein Eintrag ⇒ Caching<br />

kommt für Seite nicht zum<br />

Einsatz ......<br />

... aber natürlich doch für Inhalte des<br />

MIME-Repositories der Anwendung !<br />

Seite nur aus Server-Cache<br />

liefern wenn Request vom<br />

gleichen Browsertyp kommt<br />

Mit Objekt response der Klasse CL_HTTP_RSPONSE Verfallszeit des ICM Server-Caches steuern :<br />

- Relative Verfallszeit festlegen: response→server_cache_expire_rel( )<br />

- Absolute Verfallszeit festlegen: response→server_cache_expire_abs( )<br />

Durch statische Methoden von CL_HTTP_SERVER Invalidieren + Auffrischen des ICM Server-Cache:<br />

- Cache-Einträge für Seite : CL_HTTP_SERVER ⇒ server_cache_invalidate( )<br />

- Alle Objekte im Server Cache : CL_HTTP_SERVER ⇒ server_cache_invalidate_all( )<br />

Quelle: SAP-Bibliothek (Online-Docu) Benchmark Tests für Cache-Treffer im Hauptspeicher haben<br />

Antwortzeiten von unter einer Millisekunde pro Request und einen Gesamtdurchlauf von über 3000<br />

Requests pro Sekunde auf einer 4-CPU Hardware ergeben. MIME-Objekte werden nach dem ersten<br />

Laden automatisch im Server-Cache eingetragen. Bei Änderungen wird der Puffer invalidiert, was<br />

auch programmgesteuert erfolgen kann.<br />

Man kann sowohl für den Browser Cache als auch für den ICM Server-Cache bestimmen, wie lange<br />

die Seite im Cache gehalten werden soll. Wenn nichts angegeben wird, wird der entsprechende<br />

Cache für die Seite nicht verwendet. Das Kennzeichen Browser abhängig bedeutet, dass der Server<br />

die Seite nur aus dem Cache holt, wenn der Request vom gleichen Browser-Typ kommt. Wenn das<br />

Kennzeichen nicht gesetzt ist müssen alle von der Seite verwendeten Elemente browserunabhängig<br />

sein. Browser-Cache: Der Client kann nicht merken, wenn sich eine Seite geändert hat; er liefert bei<br />

der gleichern URL immer dieselbe Seite aus dem Cache, bis die Verfallsdauer abgelaufen ist - somit<br />

ist der Browser-Cache weniger gut kontrollierbar wie der Server-Cache.<br />

Steuerung des ICM-Server_Caches: Halten von BSPs im Server-Cache<br />

Man kann die relative Verfallszeit oder eine absolute Verfallszeit angeben sowie auch spezielle<br />

Cache-Einträge (nicht nur ganze BSP-Seiten) invalidieren - auch in Abhängigkeit von<br />

Benutzereingaben.<br />

Durch die Invalidierungs-Methoden kann man stets die Aktualität der gecachten Seite gewährleisten.<br />

Die Angaben werden vom System automatisch an andere Applikationsserver weitergeleitet, damit<br />

die Cache-Integrität gewährleistet ist. Man vermeide allzu häufige Invalidierungen, um systemweite<br />

Übertragungen an andere App-Server zu minimieren.<br />

Beispiel: response->server_cache_expire_abs( expires_abs_time = '180000 ) .<br />

Jeden Abend um 18 Uhr wird der Cache-Eintrag invalidiert. Dies kann für Dinge, die einmal täglich<br />

neu berechnet werden (z.B. aktuelle Preise), sinnvoll sein.<br />

(26)


NWAS als HTTP-Client<br />

REPORT ZCLIENTDEMO.<br />

DATA: myurl TYPE STRING,<br />

myclient TYPE REF TO IF_HTTP_CLIENT,<br />

rc TYPE I,<br />

content TYPE STRING.<br />

Auf Fehlerhandling<br />

myurl = 'http://www.sap.de'.<br />

wurde verzichet ......<br />

* Erstellen HTTP-Client :<br />

cl_http_client=>create_by_URL(<br />

exporting url = myurl<br />

importing client = myclient ).<br />

* Senden Request und Empfangen der Response :<br />

myclient→send( ).<br />

myclient→receive( ).<br />

myclient→response->get_status( importing code = rc ).<br />

* Abholen Response-Daten im Character-Format: get_cdata()<br />

* Abholen Response-Daten im Binär-Format: get_data()<br />

content = myclient→response→get_cdata( ).<br />

* Schliessen Request und Freigeben der Ressourcen :<br />

myclient→close( ).<br />

NWAS kann als Web-Client<br />

Requests an andere WebServer<br />

(auch: NWAS) absetzen und<br />

Response entgegennehmen.<br />

Zentral ist die Klasse :<br />

CL_HTTP_CLIENT<br />

Der NWAS kann auch als Client fungieren: Es kann ein Request an einen HTTP-Server gesendet<br />

und die Response empfangen werden. Der Request wird mit Methoden des Interfaces<br />

IF_HTTP_CLIENT aufgebaut, gesendet und die Response empfangen. Als HTTP-Server kann ein<br />

weiterer SAP NWAS oder auch ein beliebiger anderer Server fungieren.<br />

Um einen HTTP-Client aufzubauen muss eine gültige URL angegeben werden. Durch Methoden der<br />

Klasse CL_HTTP_CLIENT kann der Request abgesetzt und und die Response entgegengenommen<br />

werden. Es ist wichtig, mit der Methode close() die HTTP-Verbindung zu schliesen, damit die<br />

gehaltenen Systemressourcen wieder freigegeben werden.<br />

Der Returncode rc enthält den HTTP-Statuscode des Requests und müsste ausgewertet werden.<br />

"Typische" Werte sind zB :<br />

rc = 200 (OK) rc = 302 (Redirect) rc = 401 (Authentication Required)<br />

rc = 500 (Server Error)<br />

Da der NWAS auch als Web-Client fungieren kann, läßt sich durch BSP-Anwendungen auch eine<br />

Verknüpfung zwischen verschiedenen Websites herstellen. Auf dieser Basis lassen sich mit dem<br />

NWAS auch Internetportale zur kollaborativen, unternehmensübergreifenden Geschäftsprozessierung<br />

realisieren – Stichwort SOA !!<br />

(28)


Profilpflege NWAS<br />

Relevante Einstellungen des<br />

NWAS in Profildateien.pfl im<br />

NWAS-Filessystem<br />

Zur Profilpflege RZ10 :<br />

Werkzeuge → CCMS →<br />

Konfiguration → Systemprofile<br />

Infos durch Anzeige von :<br />

→ Verwaltungsdaten<br />

→ Grundpflege<br />

→ Erweiterte Pflege<br />

Hier auch festgelegt, ob ICM beim<br />

Starten der Instanz aktiviert wird +<br />

ob HTTP- und SMTP-PlugIn<br />

eingebunden und Port zugeordnet<br />

ist<br />

(30)<br />

Auf Fehlerhandling<br />

wurde verzichet ......<br />

Der NWAS wird mit einem Standard-Systemprofil ausgeliefert, dessen einzelne Parameter die<br />

Performanz des NWAS stark beeinflussen. Dort ist u.a. gepflegt, welche PlugIns des ICMs aktiviert<br />

und welche Ports ihnen zugeordnet sind. Zu erkennen in der Erweiterten Pflege ist zB der HTTP-<br />

Server-Port mit dem Wert 8065.<br />

Alle Profildateien befinden sich im Filesystem des NWAS und werden beim Starten des Systems<br />

(bzw der Instanz) ausgelesen.


Übersicht BSP-Seiten-Direktiven<br />

BSP-Direktive Beispiel Bedeutung<br />

Festlegen Server-Skriptsprache<br />

Einbinden von Seitenfragmenten<br />

Skript-Tag<br />

BSP-Kommentar<br />

Ausgabe-Tag<br />

OTR-Kurztext<br />

Text OTR-Langtext<br />

<br />

Wir listen die verschiedenen BSP-Seiten-Direktiven auf, die bei der Entwicklung von BSP-Seiten<br />

eingesetzt werden können.<br />

Bis auf das Einbinden von BSP-Extensions haben wir alle Direktiven kennengelernt<br />

(31)


Bewertung + Einordnung BSP-Technologie<br />

Nutzung bestehender<br />

SAP-Anwendungen<br />

ohne Anpassung<br />

Neue Anwendungen<br />

mittleren Umfangs<br />

Komplette<br />

Beeinflussung +<br />

Ausprogrammierung<br />

von Look & Feel<br />

Zustandslose Appl.<br />

Neue Anwendungen<br />

großen Umfangs<br />

Übernahme von SAP-<br />

Oberflächenelementen<br />

Konventioneller Browser Interner ITS<br />

Konventioneller Browser<br />

Mobile Devices mit<br />

individueller CSS-<br />

Anpassung<br />

Ausnutzen HTML5 + JS + CSS<br />

Alle Plattformen<br />

Browser<br />

BSP<br />

Konventionelle<br />

Einbettung in ABAP-<br />

Workbench und<br />

vertraute ABAP-Welt<br />

Relativ offenes<br />

Framework<br />

(32)<br />

Web Dynpro<br />

Durchgängig MVC<br />

Eigene Begrifflichkeit<br />

Vorgegebene<br />

Oberflächenelemente<br />

Geschlossenes<br />

Framework<br />

BSP ist sicherlich nicht die aktuelleste und mächtigste SAP-Web-Technologie. BSP bietet sich jedoch<br />

an, wenn kleinere und mittlere Web-Projekte mit geringem Aufwand schnell zu erstellen sind. Vorteile<br />

sind :<br />

1. Einfache Umsetzung des konventionellen ServerPage-Paradigmas<br />

2. Aufbau der Oberflächen liegt komplett in der Hand des Entwicklers<br />

3. Firmenspezifisches Look &Feel und Corporate Identity-Vorgaben lassen sich umsetzen<br />

4. Wie stark die Komplexität des zugrundeliegenden ICF-Frameworks genutzt wird, liegt in der<br />

Hand des Entwicklers


Anhang : Fortgeschrittene Techniken<br />

1. Mails aus BSP<br />

2. Programmieren eigener ICF-Services + HTTP-Handler<br />

3. Programmierung eigener BSP-Extensions (Tag Libraries)<br />

4. MVC mit BSP<br />

Im Folgenden werden fortgeschrittene Möglichkeiten im BSP-Umfeld besprochen. Zur weiteren<br />

Vertiefung verweisen wir jedoch auch auf die zur Vorlesung angegebene Literatur !<br />

(33)


Mails aus BSPs mittels BCS<br />

Verwendung Business Communication<br />

Service<br />

Voraussetzungen :<br />

1. SMTP-Plugin im ICM-Profil konfiguriert, zB :<br />

icm/server_port_1 = PROT=SMTP, PORT=25<br />

2. In TA SCOT Mailing via SMTP eingeschaltet::<br />

Grün hinterlegter Eintrag SMTP<br />

3. Zuweisung Mail-Domäne an SAP-System<br />

Beispielcoding im Report ZMAILDEMO<br />

in Paket ZBSPTEST .......<br />

(34)<br />

Erzeugte Mail und deren Zustand in<br />

SAPconnect-Administration TA SCOT<br />

überprüfbar durch Auswahl der<br />

Sendeübersicht :<br />

Hilfsmittel → Übersicht Sendeaufträge<br />

Mit Klick auf Brille email prüfen<br />

In Empfängerliste stehen Adressaten.<br />

Der NWAS verfügt über ein SMTP-Plugin, das emails direkt aus BSP-Anwendungen versenden kann,<br />

sofern der WAS an einen Mailserver gekoppelt ist. Um Mails zu senden müssen einige Objekte<br />

verschiedener Klassen durch statische Methodenaufrufe erzeugt werden:<br />

1.Mail-Auftrag erzeugen mittels Klasse CL_BCS und statischer Methode create_persistent().<br />

2.Mail-Dokument erzeugen mittels Klasse CL_DOCUMENT_BCS und statischer Methode<br />

create_document(). Mail-Text wird in interner Tabelle vom Typ character(255) verwaltet. Zu<br />

sendender Text muss in dieser Tabelle stehen.<br />

3.Erzeugtes Dokument dem Mail-Auftrag zuordnen durch Instanz-Methode set_document() der<br />

Klasse CL_BCS.<br />

4. Festlegen des Senders durch Klasse CL_SAPUSER_BCS und statischer Methode create().<br />

5.Erzeugten Sender dem Mail-Auftrag zuordnen durch Instanz-Methode set_sender() der Klasse<br />

CL_BCS.<br />

6. Festlegen des Empfängers mittels Klasse CL_CAM_ADRESS_BCS und statischer Methode<br />

create_internet_adress(), sowie Interface IF_RECIPIENT_BCS.<br />

7. Empfänger an Mailaufrag übergeben durch Instanz-Methode add_recipient() der Klasse<br />

CL_BCS.<br />

8. Mail-Auftrag freigeben durch Instanz-Methode send() der Klasse CL_BCS. Das Senden der Mail<br />

erfolgt asynchron.<br />

9. COMMIT WORK.<br />

In der SAPconnect-Administration (TA SCOT) kann der Erfolg des Mailversands überprüft werden.


Eigene HTTP- Handler<br />

Vordefinierter BSP- Handler = Klasse CL_HTTP_EXT_BSP<br />

Eigener HTTP- Handler = Jede ABAP-Klasse die if_http_extension implementiert<br />

Einziger Methode : handle_request() Zur Requestbearbeitung<br />

Eingabeparameter : Objekt server Zugriff HTTP-request/ -response<br />

Methodenaufruf : response→set_cdata() HTTP-Antwort<br />

METHOD if_http_extension~handle_request.<br />

data: text type string, info type string.<br />

info = server→request→get_header_field( '~path' ).<br />

text = ' Service erreicht! '<br />

server→response→set_status( code = 200 reason = 'OK' ).<br />

server→response→set_cdata( text ).<br />

if_http_extension~flow_rc = if_http_extension~co_flow_ok.<br />

ENDMETHOD.<br />

Attribut if_http_extension~flow_rc steuert Aufruf weiterer Behandler<br />

Request vollständig behandelt, keine weiteren Handler :<br />

if_http_extension~flow_rc = if_http_extension~co_flow_ok.<br />

Nach Ausführung Behandler weitere Behandler :<br />

if_http_extension~flow_rc = if_http_extension~co_flow_ok_others_opt.<br />

(35)<br />

Vorteil<br />

Totale Kontrolle<br />

der Request-<br />

Bearbeitung ohne<br />

BSP-LZ-Zugriffe<br />

⇓<br />

Leichtgewichtige<br />

schlanke Services<br />

Servlet-artige<br />

Programmierung<br />

BSPs sind aus Sicht des ICM ledglich eine besondere Art von HTTP-Services, die alle durch den<br />

speziellen, von SAP ausgelieferten Requestbehandler CL_HTTP_EXT_BSP bedient werden. Dieser<br />

ist dem SICF-Knoten /sap/bc/bsp zugeordnet. Wird eine BSP-Anwendung über die Workbench<br />

angelegt, so wird automatisch ein Unterknoten dazu angelegt. Dadurch ist auch automatisch<br />

sichergestellt, dass die Anwendung vom SAP-BSP-Framework, also dem Behandler<br />

CL_HTTP_EXT_BSP aufgerufen wird.<br />

Ein eigener HTTP-Handler ist einfach eine normale ABAP-Klasse die das Interface<br />

if_http_extension mit seiner einzigen Methode handle_request() implementiert. Deren<br />

Eingabeparameter ist ein server-Objekt, das HTTP-request und -response-Objekte enthält. Durch<br />

handle_request() wird ein Request bearbeitet und darin durch Aufruf der Methode<br />

response→set_cdata() eine HTTP-Antwort erzeugt. Wenn der ICM einen zugehörigen Knoten in der<br />

Service-Verwaltung gefunden hat, dann wird ein Objekt der Behandler-Klasse instantiiert und deren<br />

Methode handle_request() aufgerufen.<br />

Durch wenige Codezeilen kann ein neuer HTTP-Behandler in das ICF eingefügt werden. Vorteil<br />

selbstgeschriebener Handler ist, dass man totale Kontrolle über die HTTP-request-Auswertung und<br />

HTTP-response-Erstellung hat. Wenn die Leistungen der BSP-LZ gar nicht benötigt werden, so lassen<br />

sich auf diese Weise sehr schlanke und performante Lösungen realisieren.


Eigener HTTP- Handler in SICF<br />

ZCL_HTTP_HANDLER<br />

Beispiel für resultierende URL :<br />

Service HandlerTest angelegt in SICF direkt unter default_host :<br />

http://m65z.hcc.uni-magdeburg.de:8065/handlertest<br />

(36)<br />

In SICF muss neuer Service<br />

angelegt werden<br />

Pfad im SICF-Baum bestimmt<br />

URL des Service<br />

Eigene HTTP-Behandlerklasse<br />

ist in Handlerliste einzutragen<br />

Sollen weitere Handler im<br />

Anschluss aufgerufen werden,<br />

sind auch sie in gewünschter<br />

Reihenfolge in Handlerliste<br />

aufzunehmen<br />

Das Interface if_http_extension hat ein Attribut namens flow_rc. Dessen Wert steuert, ob nach<br />

Ausführung des Requests noch weitere Behandler aufgerufen werden sollen oder die Behandlung<br />

beendet ist. Es stehen in dem Interface dazu vordefinierte Konstanten zur Verfügung:<br />

Request ist vollständig behandelt, die response wurde komplett erstellt, es sind keine weiteren<br />

Handler (also auch nicht das BSP-Framework) aufzurufen: if_http_extension~flow_rc =<br />

if_http_extension~co_flow_ok<br />

Nach Ausführung des Behandlers sind weitere Behandler aufzurufen. Diese sind in ihrer Aufruf-<br />

Reihenfolge in der Handler-Liste des Services innerhalb der Service-Verwaltung aufgeführt:<br />

if_http_extension~flow_rc = if_http_extension~co_flow_ok_others_opt<br />

Eigene Handler sind als eigener Service in den SICF-Baum aufzunehmen und in die Handlerliste<br />

aufzunehmen. Da eigene Handler nicht zum BSP-Framework gehören, sollten sie im SICF-Baum nicht<br />

unterhalb von /sap/bc/bsp eingetragen werden, sondern unter einem separaten Pfad.<br />

Der Aufruf selbstgeschriebener Handler erfolgt mit einer URL, die neben dem Namen des Servers<br />

auch den SICF-Pfad enthält.<br />

Trägt man in der Handler-Liste auch die Klasse CL_HTTP_EXT_BSP ein, so würde im Anschluss<br />

auch das BSP-Framework aufgerufen werden.


Eigene Tag-Library = BSP-Extension<br />

* Redefinition Methode in generierter Behandler-Klasse :<br />

METHOD IF_BSP_ELEMENT~DO_AT_END.<br />

data: grussformel type string value ' Hallo, & ! ',<br />

out type ref to if_bsp_writer.<br />

out = get_previous_out( ).<br />

replace '&' in grussformel with name.<br />

out→print_string( grussformel ).<br />

ENDMETHOD.<br />

<br />

<br />

<br />

Test eigene Extension <br />

<br />

<br />

<br />

<br />

<br />

<br />

Tag-Libraries :<br />

Einheitliche Darstellung von Oberflächenelementen<br />

+ Präsentationslogik<br />

Zusammenfassung + Auslagerung<br />

aufwendiger statischer + dynamischer<br />

Elemente in Tag-Bibliothek<br />

⇒ Wiederverwendung + Lesbarkeit !<br />

⇒ Einheitliches Layout !<br />

⇒ Einbinden als selbstdefinierte<br />

parametrisierte Tags<br />

Beispiele:<br />

Tabellen + Suchen/Sortieren, Trees, Kalender, ….<br />

Workbench :<br />

(37)<br />

1. Anlegen → BSP-Extension<br />

2. Anlegen → BSP-Element<br />

3. Attribute für BSP-Element anlegen<br />

⇒ Behandlerklassen automatisch generiert<br />

4. Methode(n) der generierten Klasse durch<br />

eigenes Behandlercoding redefinieren<br />

⇒ Funktionalität + Attributbelegung<br />

5. Neue Tag-Library in BSP-Seiten<br />

anmelden + Tags aufrufen<br />

Tag Libraries sind ein wichtiges Hilfsmittel bei der Gestaltung der Benutzerschnittstelle. Sie bieten die<br />

Möglichkeit, wiederkehrende grafische Elemente einheitlich darzustellen. Auch immer gleiche Logik<br />

zur Präsentation von Daten kann in Tags ausgelagert werden. Der Name Extension bedeutet: Man<br />

kann den Grundwortschatz der Präsentierungssprache HTML um eigene Tags erweitern. BSP-<br />

Extensions kommen somit als Erweiterung der HTML-Tags daher und fügen sich besser in das<br />

Design einer HTML-Seite ein als zahlreiche Scripting-Anweisungen.<br />

Der Hauptgrund für den Einsatz von Tag Libraries ist die Wiederverwendbarkeit. Immer gleicher<br />

HTML-Code wird nicht in die Webseiten eingestreut, da man diesen auch als parametrisiertes Tag<br />

aufrufen kann. Das Layout bleibt lesbar, einheitlich und übersichtlich. Man erreicht mit Tag Libraries,<br />

gleichbleibenden Code nur einmal implementieren zu müssen.<br />

Das BSP-Element ähnelt technisch einem Funktionsbausteinaufruf. Die Schnittstelle sind die<br />

Element-Attribute. So lassen sich variable Teile des zu generierenden Codes durch entsprechende<br />

Attributwerte steuern. Auf Serverebene stellt sich eine Seite als eine Mischung von HTML- und BSP-<br />

Elementen dar. Der Server transformiert die BSPElemente in die Zielsprache HTML, die an den Client<br />

gesendet wird. Die Regeln der Transformation sind in der Implementierung der BSP-Elemente<br />

verborgen. Der für die Präsentationslogik notwendige Code wird verborgen. In der Seite wird nur das<br />

gewünschte Layoutelement, geeignet parametrisiert, angesprochen. Die Erzeugung des Codes wird<br />

an die Elementbehandlerklasse delegiert und gehört somit nicht mehr zur Präsentationsinformation<br />

der jeweiligen Seite.<br />

Dadurch, dass die Elementbehandlerklasse von einer automatisch generierten Basisklasse erbt,<br />

enthält sie automatisch Implementierungen des Interfaces IF_BSP_ELEMENT. Diese sind jedoch alle<br />

leer. Damit ein BSP-Element etwas Sinnvolles tut, muss man mindestens eine Methode dieses<br />

Interface redefinieren und mit eigenem Code versehen - wie es oben mit der Methode do_at_end( )<br />

geschieht.


Model View Controller Paradigma MVC-Design Pattern<br />

Konsequente Entkopplung von<br />

Ablaufsteuerung, Präsentation<br />

(Layout) und Geschäftslogik in<br />

separaten Objekten<br />

Request<br />

Response<br />

Methodenaufrufe<br />

Datenfluss<br />

Ereignisse<br />

Kontrollfluss<br />

Daten-<br />

Weitergabe<br />

View-Auswahl<br />

View-Aufruf<br />

Controller<br />

View<br />

Zustandsabfragen<br />

<br />

<br />

Steuerung<br />

Steuerung<br />

Kontrollfluss<br />

Kontrollfluss<br />

der<br />

der<br />

Anwendung<br />

Anwendung<br />

<br />

<br />

Events<br />

Events<br />

behandeln<br />

behandeln<br />

<br />

<br />

Daten<br />

Daten<br />

an<br />

an<br />

View<br />

View<br />

geben<br />

geben<br />

/<br />

/<br />

von<br />

von<br />

View<br />

View<br />

holen<br />

holen<br />

<br />

<br />

Übersetzen<br />

Übersetzen<br />

Benutzereingaben<br />

Benutzereingaben<br />

für<br />

für<br />

Model<br />

Model<br />

<br />

<br />

Auswahl<br />

Auswahl<br />

passende<br />

passende<br />

View<br />

View<br />

für<br />

für<br />

Response<br />

Response<br />

Aufruf<br />

Model<br />

Zustandsänderungen<br />

Quelle: OIO, Kursmaterial<br />

<br />

<br />

Visualisieren<br />

Visualisieren<br />

der<br />

der<br />

Anwendungsdaten<br />

Anwendungsdaten<br />

<br />

<br />

Darstellung<br />

Darstellung<br />

+<br />

+<br />

Benutzerinteraktion<br />

Benutzerinteraktion<br />

<br />

<br />

Senden<br />

Senden<br />

von<br />

von<br />

Benutzereingaben<br />

Benutzereingaben<br />

an<br />

an<br />

Controller<br />

Controller<br />

(38)<br />

<br />

<br />

Container<br />

Container<br />

für<br />

für<br />

Geschäftsdaten<br />

Geschäftsdaten<br />

und<br />

und<br />

Geschäftslogik<br />

Geschäftslogik<br />

<br />

<br />

Kapselung<br />

Kapselung<br />

Anwendungszustand<br />

Anwendungszustand<br />

<br />

<br />

Eigentliche<br />

Eigentliche<br />

Datenverarbeitung<br />

Datenverarbeitung<br />

durch<br />

durch<br />

Geschäftslogik<br />

Geschäftslogik<br />

<br />

<br />

Information<br />

Information<br />

View<br />

View<br />

bei<br />

bei<br />

Änderung<br />

Änderung<br />

Server<br />

Architektur entsteht immer durch Zerlegung eines Ganzen in einzelne Komponenten und Definition<br />

von deren strukturierten Zusammenwirken. Dabei orientiert man sich an typischen Mustern,<br />

sogenannten Design Patterns.<br />

MVC führt zu einer besseren organisatorischen und logischen Strukturierung der Anwendung. Es wird<br />

das Prinzip "Separation of Concerns" umgesetzt, wodurch auch die Wartbarkeit deutlich zunimmt.<br />

Ohne andere Komponenten zu beeinflussen, können zB die Views oder Teile des Models<br />

ausgetauscht werden.


MVC mit BSP<br />

Einbettung in Workbench +<br />

Forward-Navigation<br />

Controller<br />

View<br />

Model<br />

BSP-Anwendung zugeordnet Anlegen→ Controller<br />

BSP-Anwendung zugeordnet Anlegen→ Controller<br />

<br />

<br />

Ablaufsteuerung<br />

Ablaufsteuerung<br />

(Flow<br />

(Flow<br />

Logic)<br />

Logic)<br />

der<br />

der<br />

Anwendung<br />

Anwendung<br />

durch<br />

durch<br />

entsprechende<br />

entsprechende<br />

Methoden<br />

Methoden<br />

–<br />

–<br />

analog<br />

analog<br />

BSP-Eventhandlern<br />

BSP-Eventhandlern<br />

<br />

<br />

Klassen<br />

Klassen<br />

erben<br />

erben<br />

von<br />

von<br />

ICF-Basisklasse<br />

ICF-Basisklasse<br />

CL_BSP_CONTROLLER2<br />

CL_BSP_CONTROLLER2<br />

<br />

<br />

Können<br />

Können<br />

Views<br />

Views<br />

aufrufen<br />

aufrufen<br />

sowie<br />

sowie<br />

Model-Objekt<br />

Model-Objekt<br />

erzeugen<br />

erzeugen<br />

+<br />

+<br />

ansprechen<br />

ansprechen<br />

<br />

<br />

Aufrufbar<br />

Aufrufbar<br />

mit<br />

mit<br />

eigener<br />

eigener<br />

URL<br />

URL<br />

Endung<br />

Endung<br />

.do<br />

.do<br />

<br />

<br />

Coding<br />

Coding<br />

hinterlegen<br />

hinterlegen<br />

durch<br />

durch<br />

Überschreiben<br />

Überschreiben<br />

geerbter<br />

geerbter<br />

Methoden<br />

Methoden<br />

<br />

<br />

Normale<br />

Normale<br />

ABAP-OO-Klassen<br />

ABAP-OO-Klassen<br />

<br />

<br />

Erben<br />

Erben<br />

von<br />

von<br />

CL_BSP_MODEL<br />

CL_BSP_MODEL<br />

<br />

<br />

Zahlreiche<br />

Zahlreiche<br />

geerbte<br />

geerbte<br />

Methoden<br />

Methoden<br />

<br />

<br />

Frei<br />

Frei<br />

definierbare<br />

definierbare<br />

eigene<br />

eigene<br />

Methoden<br />

Methoden<br />

+<br />

+<br />

Attribute<br />

Attribute<br />

zur<br />

zur<br />

Darstellung<br />

Darstellung<br />

der<br />

der<br />

Logik<br />

Logik<br />

<br />

<br />

Direkt<br />

Direkt<br />

via<br />

via<br />

ClassBuilder<br />

ClassBuilder<br />

angelegt<br />

angelegt<br />

<br />

<br />

Aufrufe<br />

Aufrufe<br />

erfolgen<br />

erfolgen<br />

aus<br />

aus<br />

Methoden<br />

Methoden<br />

der<br />

der<br />

Controller-Klasse<br />

Controller-Klasse<br />

<br />

<br />

BSP-Anwendung<br />

BSP-Anwendung<br />

zugeordnet Anlegen → Seite → View<br />

zugeordnet Anlegen → Seite → View<br />

<br />

<br />

Haben<br />

Haben<br />

keine<br />

keine<br />

eigene<br />

eigene<br />

URL<br />

URL<br />

–<br />

–<br />

nicht<br />

nicht<br />

direkt<br />

direkt<br />

aufrufbar<br />

aufrufbar<br />

<br />

<br />

Vom<br />

Vom<br />

Controller<br />

Controller<br />

aufgerufen<br />

aufgerufen<br />

+<br />

+<br />

mit<br />

mit<br />

Daten<br />

Daten<br />

versorgt<br />

versorgt<br />

<br />

<br />

Haben<br />

Haben<br />

Eigenschaften,<br />

Eigenschaften,<br />

Layout,<br />

Layout,<br />

Seitenattribute<br />

Seitenattribute<br />

<br />

<br />

Keine<br />

Keine<br />

Eventhandler,<br />

Eventhandler,<br />

keine<br />

keine<br />

Auto-Seitenattribute<br />

Auto-Seitenattribute<br />

Der Controller beinhaltet die Steuerungslogik der Anwendung. Er reagiert auf User-Input und<br />

Ereignisse und verteilt Aufgaben an die View und das Model. Innerhalb BSP besteht der Controller<br />

aus zwei separaten Teilen: 1. Das Controller-Eigenschaften-Objekt mit den Attributen des<br />

Controllers (Verwaltungsinformationen, URL, Zustandsverhalten) 2.Eine zugeordnete ABAP-OO-<br />

Klasse, die von der BSP-Systemklasse CL_BSP_CONTROLLER2 erbt. Diese Klasse enthält die<br />

eigentliche Controller-Logik; in ihr wird der ABAP-Code hinterlegt, der die Aufgaben des Controllers<br />

umsetzt.<br />

Im Gegensatz zu BSP-Seiten können Views nicht direkt von Außen aufgerufen werden.<br />

In einer BSP-Anwendung können sowohl Seiten mit Ablauf-Logik als auch Controller und Views sowie<br />

Model-Klassen vorhanden sein. Je komplexer eine BSP-Anwendung ist, desto eher lohnt sich der<br />

zusätzliche Aufwand des MVC-Patterns.<br />

Zur Umsetzung von MVC in einer BSP-Anwendung sind ABAP-OO-Kenntnisse erforderlich,<br />

insbesondere auch Vererbung, Überschreiben von Methoden und das Prinzip der Polymorphie. Da<br />

separate ABAP-OO-Klassen angelegt werden, bekommt man in der Workbench alle Elemente der<br />

entsprechenden BSP-Anwendung (inklusive der Klassen) nur in der Paket-Sicht zu Gesicht.<br />

Auf die sehr mächtigen Möglichkeiten des DataBindings zwischen Views und Models können wir hier<br />

nicht eingehen. Wir verweisen dafür auf die Literatur, insbesondere auf :<br />

B. McKellar T.Jung : Advanced BSP Programming, Kapitel 13<br />

(39)


MVC mit BSP<br />

Controller-Eigenschaften<br />

View-Seiten haben keine eigene URL, keine<br />

Eventhandler, keine Auto-Seitenattribute ….<br />

Controller-Bestandteile = (40)<br />

1. Controller-Eigenschaften +<br />

2. Controller-Klasse / -Objekt<br />

Bei Anlegen von Controllern in<br />

Workbench wird Name der<br />

Controller-Klasse genannt<br />

Durch Vorwärtsnavigation kann<br />

man im ClassBuilder Methoden<br />

überschreiben<br />

CL_BSP_CONTROLLER2 dabei<br />

automatisch als Superklasse<br />

gesetzt<br />

View-Eigenschaften<br />

Im Gegensatz zu BSP-Seiten können Views nicht direkt von Außen aufgerufen werden. Sie verfügen<br />

auch nicht über Eventhandler – denn die Geschäftslogik liegt im Model und die Kontrollflusssteuerung<br />

im Controller.


Verarbeitungs-Ablauf im Controller<br />

Controller repräsentiert Ablaufsteuerung einer BSP-Seite (Flow Logic)<br />

Durch Methoden und deren definierten Aufruf realisiert - BSP-Runtime besorgt Aufrufe<br />

Analog : Eventhandler einer konventionellen BSP-Seite mit Ablauflogik<br />

Konventionelle BSP-Seite<br />

OnCreate<br />

OnInitialization<br />

OnRequest<br />

Layout<br />

OnInputProcessing<br />

OnManipulation<br />

Hier nur Grundlagen. Mächtige Möglichkeiten für Data-<br />

Bindung und automatischer Datenübertragung :<br />

B. McKellar T.Jung : Advanced BSP Programming<br />

Kapitel 13<br />

Controller-Methoden und ihre Bedeutung :<br />

MVC-Controller-Klasse<br />

DO_INIT<br />

DO_INITATTRIBUTES<br />

DO_REQUEST<br />

DO_HANDLE_DATA<br />

DO_HANDLE_EVENT<br />

DO_FINISH_OUTPUT<br />

CALL_VIEW , Dispatch_Input<br />

Festlegen der Ablauflogik durch Überschreiben<br />

(Redfinieren) dieser von CL_BSP_CONTROLLER2<br />

geerbten Methoden in der eigenen Controller-Klasse<br />

ClassBuilder : Redine-Button im Methoden-Tab für selektierte Methode<br />

DO_INIT : Diese Methode wird bei der Erzeugung der Controller-Instanz aufgerufen. Hier kann z. B. ein Objekt<br />

einer Model-Klasse erzeugt werden. Wird bei stateful-Anwendungen nur einmal gerufen.<br />

DO_INITATTRIBUTES : Ist mit dem BSP-Eventhandler Onlnititialization vergleichbar.<br />

DO_REQUEST : Zentrale Methode für Eingabe-/Ausgabe-Steuerung. Steuert die folgenden spezialisierten<br />

Methoden für Input-Verarbeitung:<br />

DO_HANDLE_DATA sorgt dafür, dass die Model-Klasse mit Daten gefüllt wird. Daten der Formfelder und<br />

Nachrichten werden übergeben. DO_HANDLE_EVENT gibt den Parameter GLOBAL_EVENT aus und füllt im<br />

Fall von Events, die von BSP-Elementen ausgelöst werden, das HTMLB-Event-Objekt. DO_FINISH_INPUT<br />

reagiert mithilfe des GLOBAL _EVENT-Parameters auf Ereignisse. Es ist die letzte Methode der<br />

Eingabeverarbeitung.<br />

Eintreffende Client-Requests werden direkt an den Hauptcontroller geleitet mittels des URL-Aufrufs. Nach<br />

DO_INIT und DO_INITATTRIBUT wird die zentrale Methode DO_ REQUEST aufgerufen. Von dieser Methode<br />

können eingehende Daten an eventuelle Subcontroller weitergeleitet werden. Dies übernimmt die Methode<br />

DISPATCH_INPUT. Sie liest die Daten aus den Formfeldern des Request-Objekts aus und leitet sie an die<br />

jeweils adressierten Subcontroller weiter. Ferner sogt diese Methode dafür, dass die Methoden<br />

DO_HANDLE_DATA, DO_HANDLE_EVENT und DO_FINISH_INPUT aufgerufen werden<br />

Daten, die für den Hauptcontroller bestimmt sind, werden innerhalb von DO_REQUEST durch die Methoden<br />

DO_HANDLE_DATA, DO_HANDLE_EVENT und DO_FINISH_INPUT verarbeitet. Subcontroller verarbeiten<br />

ihnen zugeordnete Daten ebenfalls über ihre Implementation dieser Methoden.<br />

Zur Ausgabeverarbeitung werden ein oder mehrere Views erzeugt und aufgerufen. Dies wird mittels der Methode<br />

CALL_VIEW ausgeführt. Controller können auf den Status inaktiv gesetzt oder neue Controller erzeugt werden.<br />

(41)


MVC mit BSP – typische Strukturen<br />

Model<br />

Controller<br />

Aufrufe<br />

Ein Controller kann mehrere Views<br />

nacheinander ansteuern ……<br />

BSP-Anwendung<br />

Controller 1<br />

Model 1<br />

Aufruf<br />

View 1<br />

View 1<br />

View 1<br />

View 1<br />

Controller 2<br />

Model 2<br />

Aufruf<br />

View 2<br />

BSP-Seite<br />

Model<br />

Controller<br />

Ein übergeordneter Haupt-Controller<br />

steuert die Verteilung der Anwendungslogik<br />

durch Aufruf von Sub-Controllern ….<br />

Controller 3<br />

Model 3<br />

Aufruf<br />

View 3<br />

Aufrufe<br />

Model 1<br />

Model 2<br />

Model 3<br />

Jede einzelne "BSP-Seite" implementiert alle<br />

drei MVC-Komponenten. Views durch<br />

zugeordnete Controller gerufen …..<br />

(42)<br />

Controller 1<br />

Controller 2<br />

Aufruf<br />

View 2<br />

Aufruf<br />

View 1<br />

Controller 3<br />

Aufruf<br />

View 3<br />

In einer BSP-Anwendung können sowohl Seiten mit Ablauf-Logik als auch Controller, Models und<br />

Views vorhanden sein. Aus einem Controller können auch Controller anderer BSP-Anwendungen<br />

aufgerufen werden.<br />

Je komplexer eine BSP-Anwendung ist, desto eher lohnt sich der zusätzliche Aufwand des MVC-<br />

Patterns. Es gibt eine Vielzahl von Kombinationsmöglichkeiten. Insgesamt nimmt die Wartbarkeit der<br />

Anwendung zu, da eine saubere Trennung und Strukturierung nach MVC-Anteilen besteht.<br />

Einzelnen Komponenten können relativ leicht ausgetauscht werden.<br />

Da der Umgang mit dem MVC-Pattern relativ komplex ist und Einarbeitungszeit erfordert, hat SAP<br />

im WebDynpro-Programmiermodell die Anwendungsentwicklung durch grafische Entwicklungsanteile<br />

vereinfacht, durch die das MVC-Grundgerüst automatisch generiert wird.<br />

Durch den Controller können Views aufgerufen und an deren Seitenattribute Daten übergeben<br />

werden. Dazu könnte auch die Referenz auf ein Model-Objekt gehören! (Es ist aber auch möglich, den<br />

HTML-Datenstrom direkt in der Methode do_request() mit der Methode write() zu erzeugen.)<br />

Aus einem Controller kann ein SubController-Objekt erzeugt und diesem auch gleich ein Model-<br />

Objekt übergeben werden. Dazu dienen die geerbten Methoden create_controller( ) und<br />

set_model().<br />

SubController können auch dynamisch aktiviert und deaktiviert werden, so dass sie nicht<br />

überflüssigerweise abgearbeitet werden. Dazu dient ebenfalls eine geerbte Methode:<br />

controller_set_active( controller_id = 'sub1' active = 0 ).


Controller-Methoden überschreiben<br />

METHOD do_init. " Einbinden Model-Objekt, DB-Zugriffe, …..<br />

model ?= create_model( model_id = 'm1' class_name = 'Z_M_DEMO' ).<br />

model→carrid = 'LH'.<br />

model→read_flight_data( ). " Datenbeschaffung + Geschäftslogik ……<br />

ENDMETHOD.<br />

Zentrale Methoden der Controller-Klasse<br />

METHOD do_request. " Redefintion / Überschreiben<br />

data: view type ref to if_bsp_page. " View-Objekt<br />

data: input type string.<br />

input = request→get_form_field( name = 'input' ).<br />

dispatch_input( ). " Trigger Eingabe-Verarbeitung + Events<br />

if input = 'Einstieg'.<br />

view = create_view( view_name = 'main.htm' ). call_view( view ).<br />

else. " View mit Werten versehen<br />

view = create_view( view_name = 'next.htm' ).<br />

view→set_attribute( name = 'text' value = sy-uname ).<br />

call_view( view ).<br />

endif.<br />

ENDMETHOD.<br />

(43)<br />

Ziel :<br />

Erzeugen Modell- und View-Objekte.<br />

Model-Objekte als Controller-Attribute<br />

verwaltet<br />

Möglichkeiten :<br />

1. Request-Datenzugriff, View<br />

erzeugen, View-Seitenattribute<br />

übergegen, View rufen.<br />

2. Aus Controller verschiedene Views<br />

aufrufbar.<br />

3. Controller kann andere Controller<br />

aufrufen. Hierarchie von Controllern !<br />

4. Aus View sind Controller aufrufbar<br />

oder Redirects möglich ……..<br />

View next.htm<br />

<br />

<br />

<br />

Aufruf View <br />

<br />

<br />

Benutzer <br />

<br />

<br />

Eine Controllerklasse verfügt typischerweise über Attribute vom Typ der verwendeten Models und instanziiert<br />

entsprechende Objekte in seiner Methode do_init(). Jeder erzeugten Model-Instanz wird eine ID gegeben. Auf diese Weise<br />

sind sie auch in dem geerbten Attribut M_MODELS des Controller-Objekts unterscheidbar. Es existieren geerbte Methoden<br />

SET_MODEL, GET_MODEL, CREATE_MODEL, DELETE_MODEL im Controller, durch die gespeicherte Liste der Model-<br />

Objekte verwaltet werden kann. Der Controller kontrolliert somit auch die Erzeugung von Model-Objekten und deren<br />

Lebensdauer.<br />

MVC wird typischerweise in stateful BSP-Anwendungen verwendet. Grund ist, dass es aufwendig ist, in stateless-<br />

Anwendungen die erforderlichen Controller- und Model-Objekte immer wieder neu zu erzeugen und insbesondere deren<br />

korrekten internen Zustand konsistent zu rekonstruieren. Das Controller-Objekt enthält als Attribut eine Tabelle mit<br />

Referenzen auf erzeugte Model-Objekte. In stateless-Anwendungen müsste diese Tabelle erst immer wieder hergestellt und<br />

die darin enthaltenen Model-Objekte zustandsmäßig korrekt rekonstruiert werden. Allerdings existieren Techniken mit denen<br />

man Model-Instanzen zwischenspeichern und wiederherstellen kann. Dazu gehören die Serialisierung und Deserialisierung<br />

von Model-Objekten als XML-Strings und deren Zwischenspeicherung als Server-Cookie.<br />

Der Aufruf der Methode dispatch_input() sogt dafür, dass alle für die Eingabe-Verarbeitung erforderlichen Events ausgelöst<br />

werden und alle dafür vorgesehenen Controller-Methoden aufgerufen werden. Diese Methode muss nur einmal aus dem<br />

Haupt-Controller aufgerufen werden.<br />

Aus einem Controller können andere Controller gerufen werden, so dass neben Haupt-Controllern auch Sub-Controller<br />

definiert werden können:<br />

data: ctl type ref to zcl_bsp_subcontroller .<br />

ctl ?= create_controller( controller_name = 'sub1.do' controller_id = 'sub' ) .<br />

ctl→testdata = 'Irgendwas'.<br />

call_controller( controller_instance = ctl ). " Aufruf aus Hauptcontroller. Dieser behält Kontrolle<br />

* navigation->goto_page( 'sub1.do' ). " Navigation zu Subcontroller: Hauptcontroller wird abgebaut:<br />

Auch aus einer View lässt sich mittels BSP-Extensions eine anderer Controller aufrufen :<br />

<br />

Auch Redirects sind möglich:

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!