Versionsvergleich für APEX-Anwendungen - Its

Werbung
Auf der Suche nach dem kleinen Unterschied:
Versionsvergleich für APEX-Anwendungen
Sabine Heimsath
its-people GmbH
Frankfurt
Schlüsselworte
Oracle Application Express, APEX, Versionsverwaltung, Diff-Tool, Dateivergleich
Einleitung
Wer im professionellen Umfeld mit Apex arbeitet, kommt oft in die Situation,
- dass andere Entwickler ebenfalls Änderungen in der Entwicklungsumgebung vornehmen
- dass undokumentierte Änderungen in der Produktivumgebung vorgenommen wurden aber nicht
in der Entwicklungsumgebung nachgezogen werden oder
- dass beim Einchecken in die Versionsverwaltung ein Kommentar gepflegt werden soll, der die
aktuellen Änderungen beschreibt.
In diesen Fällen kann man versuchen, sich mit bordeigenen Mitteln zu behelfen.
Hier bieten sich zunächst
- die Application History (Applikationsansicht -> Utilities -> Change History),
- die Page History (Seitenansicht -> Utilities -> History) oder
- der Applikationsvergleich (Application Builder -> Cross Application Reports ->
Application Comparison) an.
Die Historien stoßen allerdings – auch wenn man alle verfügbaren Spalten einblendet – schnell an
ihre Grenzen. Der Application Builder loggt Aktionen von Entwicklern einerseits sehr unspezifisch
und andererseits auch dann, wenn eigentlich keine Änderung stattfand. So wird zum Beispiel das
Springen von einem Tab zum nächsten in einer Report Definition auf jeden Fall als Änderung
protokolliert, weil dabei gespeichert wird. Dadurch entstehen sehr viele irrelevante Einträge.
Die relevanten Einträge sind hingegen meistens nicht detailliert genug. Es gibt zum Beispiel keine
Information dazu, welches Attribut einer Page/eines Buttons/eines Items geändert wurde, und somit
gibt es auch keine Möglichkeit, den alten und den neuen Wert zu vergleichen.
Der Applikationsvergleich setzt voraus, dass beide Versionen im gleichen Workspace installiert sind.
Dies bietet sich innerhalb einer Entwicklungsumgebung durchaus an. Aber auch hier sind die
angebotenen Informationen eher dürftig, und es werden viele falsch positive Unterschiede angezeigt,
wie man schnell feststellt, wenn man versucht, vermeintliche Unterschiede aus dem Report in der
Entwickleransicht des jeweiligen Objektes nachzuvollziehen.
Ein Lösungsansatz
Um herauszufinden, was sich tatsächlich geändert hat, bietet sich das Apex-Anwendungs-Exportfile
an. Es ist leicht lesbar, wenn man PL/SQL beherrscht, und beinhaltet sämtliche Informationen, die die
Apex-Applikation beschreiben.
Die Objekte der Datenbankebene sind im Normalfall nicht enthalten. Man kann sie explizit als
Supporting Objects exportieren, aber da sie häufig ohnehin separat versioniert werden, werden sie hier
nicht betrachtet.
Um den Code interpretieren zu können, muss man ein wenig „Übersetzungsarbeit“ leisten. Die
Optionen, die bei der Deklaration im Application Builder angeboten werden (in der jeweils
eingestellten Sprache), werden im Export mit englischen Konstantennamen ausgegeben.
Die Typen der Report-Spalten werden damit z. B. zu:
Standard Report Column
Display as Text (based on LOV, does not save
state)
Display as Text (saves state)
Display as Text (escape special characters,
does not save state)
Date Picker (Classic)
Date Picker
Text Field
Text Area
Select List (static LOV)
Select List (named LOV)
Select List (query based LOV)
… und weitere
WITHOUT_MODIFICATION
TEXT_FROM_LOV
DISPLAY_AND_SAVE
ESCAPE_SC
DATE_POPUP
DATE_PICKER
TEXT
TEXTAREA
SELECT_LIST
SELECT_LIST_FROM_LOV
SELECT_LIST_FROM_QUERY
Abb. 1: Beispiel für Konstantennamen: Reportspalten-Typen
Die Apex-Exportfiles
Es gibt verschiedene Arten von Export-Files. Bei allen handelt es sich um PL/SQL-Skripte, die es ermöglichen, die jeweiligen Objekte (oder eine komplette Applikation) in einer anderen Umgebung neu
anzulegen.
Hier wird zunächst der Export einer gesamten Applikation behandelt (im Bild rot umrandet).
Der Export einzelner Seiten beinhaltet keine zusätzliche Information, die nicht auch im Applikationsexport enthalten ist.
Abb. 2: Verschiedene Exportmöglichkeiten im Apex Builder
Ein Exportfile beginnt mit Informationen zur Anwendung, zum Exportzeitpunkt und zum User. Dann
folgt eine Statistik über die Bestandteile der Applikation, z. B. Anzahl der Pages, der Items, der
Prozesse und weitere.
Danach folgen Anweisungen zum Setzen von Umgebungsvariablen. In der Zielumgebung wird eine
eventuell vorhandene Instanz der Applikation gelöscht. Dieser Teil kann in den meisten Fällen
ignoriert werden.
Interessant wird es etwa ab Zeile 150, denn hier wird damit begonnen, die Applikation neu aufzubauen.
Die Struktur des Applikationsexports
Im folgenden „Listing“ findet sich die Struktur des Exports einer kleinen Anwendung mit Beschreibung der einzelnen Aufrufe. Die Parameter werden weiter unten erläutert.
Beim Analysieren der Datei sollte man wissen, dass eine Applikation häufig als ‚flow‘ referenziert
wird, eine Seite als ‚page‘ oder ‚step‘ und eine Region auch als ‚plug‘ bezeichnet wird.
wwv_flow_api.create_flow
wwv_flow_api.create_user_interface
wwv_flow_api.create_plugin_setting
wwv_flow_api.create_icon_bar_item
wwv_flow_api.create_tab
Anlegen der Anwendung
wwv_flow_api.create_page
wwv_flow_api.create_page_plug
wwv_flow_api.create_page_plug
wwv_flow_api.create_page_button
wwv_flow_api.create_page_button
wwv_flow_api.create_page_branch
wwv_flow_api.create_page_item
wwv_flow_api.create_page_process
Anlegen der ersten Seite
Anlegen zweier Regionen
wwv_flow_api.create_page
wwv_flow_api.create_page_plug
wwv_flow_api.create_flash_chart5
wwv_flow_api.create_flash_chart5_series
wwv_flow_api.create_page
wwv_flow_api.create_report_region
wwv_flow_api.create_report_columns
wwv_flow_api.create_report_columns
wwv_flow_api.create_report_columns
wwv_flow_api.create_page
wwv_flow_api.create_page_plug
wwv_flow_api.create_page_button
wwv_flow_api.create_page_da_event
wwv_flow_api.create_page_da_action
wwv_flow_api.create_list
wwv_flow_api.create_list_item
wwv_flow_api.create_list_item
wwv_flow_api.create_list_item
Anlegen eines Elements im Menü rechts oben
Anlegen des Standard-Tabs
Anlegen zweier Buttons
Anlegen eines Branches
Anlegen eines Items
Anlegen eines Page Processes
Anlegen eines Flash-Diagramms
… mit einer Datenreihe
Anlagen einer Reportregion
… mit den zugehörigen Reportspalten
Anlegen eines Dynamic Action Events
… mit der zugehörigen Action
Anlegen einer Liste
… mit den zugehörigen Items
wwv_flow_api.create_menu
wwv_flow_api.create_template
wwv_flow_api.create_button_templates
wwv_flow_api.create_plug_template
Anlegen verschiedener Shared Components
… unter anderem Templates
… für die unterschiedlichsten Objekte (Pages,
Lists, Reports u. a.)
wwv_flow_api.create_theme
wwv_flow_api.create_shortcut
wwv_flow_api.create_authentication
… und andere Objekte, die auch unter diesen
Namen Application Builder wiederzufinden sind
Abb. 3: Struktur einer Export-Datei
Zunächst wird die Applikation mit der Prozedur wwv_flow_api.create_flow angelegt. Die Parameter findet man im Application Builder unter Edit Application Properties in den Reitern Definition,
Security und Globalization. Die Parameternamen sind ziemlich sprechend gewählt, so dass man sie
den Elementen aus der GUI leicht zu ordnen kann.
Das Beispiel unten zeigt dies für die Einstellungen Logging, Feedback, Primärsprache, Quelle der
Applikationssprache und die Versionsangabe zur Sicherstellung der Kompatibilität.
Abb. 4: Zuordnung Apex-Builder-Elemente zu Programmzeilen
Wenn die Applikation per create_flow angelegt wurde, können die Pages angelegt werden. Wie man
sieht, heißen die Parameter fast genauso wie im Application Builder:
Abb. 5: Definition einer Page
Auf dieser Seite befindet sich unter anderem eine Reportregion. Im gekürzten Beispiel unten erkennt
man zunächst die Definition des zugrunde liegenden SQL-Statements in der Variable s, dann folgt das
Anlegen des Reports mit Applikations-ID (p_flow_id), der Page-ID (p_page_id), dem Regionsnamen
(p_region_name), dem Template, das hier über eine ID referenziert wird, und der Display-Sequence,
die den Platz der Region in der Rendering-Reihenfolge bestimmt.
Abb. 6: Beispiel: Definition einer Reportregion (gekürzt)
Jede Spalte des Reports wird mit einem eigenen Aufruf der Prozedur (create_report_columns)
definiert:
Abb. 7: Beispiel: Definition einer Reportspalte
Neben den schon beschriebenen Parametern sieht man hier die Werte, die man im Application Builder
in der Spaltendefinition zu sehen bekommt, z. B. die Spaltenüberschrift (p_column_heading), die Ausrichtung (p_column_alignment und p_heading_alignment), und Art der Darstellung (p_display_as), in
diesem Fall WITHOUT_MODIFICATION was der ‚Standard Report Column‘ in der GUI entspricht.
Das Häkchen für ‚Show‘ wird in diesem Fall zu p_hidden_column=‘N‘.
Der Vergleich
Hat man zwei Export-Files ein und derselben Applikation, die zu unterschiedlichen Zeitpunkten
exportiert wurden, ist der Vergleich recht einfach:
Abb. 8: Beispiel: Änderungen, die an einem Page Item vorgenommen wurden
Hier sieht man, dass an dem Item mehrere Veränderungen vorgenommen wurden: Außer dem Default
Wert wurden die Definition der LOV und Text und Wert für den NULL-Wert geändert.
Ein weiterer Fall:
Abb. 9: Änderungen, die an einer Page vorgenommen wurden
Anscheinend wurde hier eine Änderung wieder rückgängig gemacht – oder der Nutzer hat zwischen
den Tabs geblättert und dadurch das erneute Abspeichern der bestehenden Werte ausgelöst.
Hat man zwei Export-Files einer Applikation vorliegen, zum Beispiel aus zwei verschiedenen Umgebungen, sieht ein einfacher Textvergleich mit einem Diff-Tool am Anfang etwa so aus:
Abb. 10: Textvergleich ohne Konfiguration: Screenshot aus Beyond Compare
Das Problem fällt sofort ins Auge: Allein durch den Export und den Import unter neuer ApplikationsID oder in einem anderen Workspace, verändern sich die IDs aller Objekte, so dass man fast nur rote
Zeilen sieht. Mit einem Diff-Tool, das Reguläre Ausdrücke beherrscht, kann man diese Zeilen
ausblenden, um sich auf die wichtigen Unterschiede konzentrieren zu können. In diesem Fall wurde
Beyond Compare 3.0 verwendet.
Konfiguration des Diff-Tools
Um in Beyond Compare die Markierung der „irrelevanten“ Zeilen zu unterdrücken, sind unter
Menüpunkt 'Session'
-> Untermenüpunkt 'Session Settings'
-> Reiter 'Importance'
-> Button 'Edit Grammar'
einige Einträge vorzunehmen.
Im Reiter ‘Grammar’ sind standardmäßig schon einige Einträge für SQL-Dateien vorhanden (siehe
unten). Die neu anzulegenden Einträge sind rot eingerahmt:
Abb. 11: Beschreibung der nicht relevanten Zeilen mit regulären Ausdrücken
Hierfür klickt man den ‘New…’ Button, und legt dann nacheinander die folgenden Einträge an. Die
Bezeichnungen sind frei wählbar. Ein einheitliches Präfix vereinfacht natürlich die Selektion.
Apex_ID_Line
\d{15,}\s?\+\s?wwv_flow_api.g_id_offset
Apex_Prompt_Line
prompt .+ \d+
Apex_Upd_Line
\s{0,5}.p_last_upd_yyyymmddhh24miss => '\d+'
Diese Einträge dienen dazu, die Unterschiede zu definieren, die beim Vergleich als unwichtig
eingestuft werden sollen. Nach dem Anlegen müssen im Reiter ‘Importance’ die Häkchen vor den neu
angelegten Apex-Einträgen entfernt werden, damit Beyond Compare weiß, dass sie als unwichtig
einzuordnen sind:
Abb. 12: Deselektion der nicht relevanten Zeilen
Problematisch wird es, wenn auf einer Seite sehr viele Pages dazugekommen sind (oder gelöscht
wurden); denn dann ist es für das Tool schwierig, die Dateien korrekt auszurichten.1
Man kann Beyond Compare bei der Ausrichtung unterstützen, indem man bei der Definition der
Grammatik bestimmte Zeilen gewichtet. Für Apex-Exporte bietet es sich zum Beispiel an, die PromptZeile vor jeder Seitendefinition sehr stark zu gewichten, da die Seitennummern während der
Lebenszeit einer Anwendung im Normalfall keinen großen Änderungen unterworfen sind.
Diese Gewichtung kann auch im Reiter ‚Grammar‘, im unteren Teil ‚Line weights‘ über ‚New...‘ oder
‚Edit...‘ vorgenommen werden:
Abb. 13: Ankerpunkte zum Ausrichten festlegen
Text matching:
^prompt
...PAGE \d+:
Ganz wichtig: Nicht vergessen, im unteren Teil des Fensters ‚Session Settings‘ festzulegen, dass die
Gültigkeit sich auf jede Session bezieht, damit man sich die Arbeit nicht mehrfach machen muss:
Abb. 14: Text Compare – Session Settings dauerhaft verfügbar machen
Die Konfiguration des Diff-Tools ist damit beendet.
1
Dieses Problem tritt nicht auf, wenn die Sourcen grundsätzlich mit dem ApexSplitter aufgeteilt werden.
Wenn nun die ‘unwichtigen’ Einträge mit dem Button
ausgeblendet werden, bekommen wir ein
viel entspannteres Bild. Und plötzlich kann man die tatsächlichen Änderungen auf einen Blick
erkennen, so wie im Beispiel unten:
Abb. 15: Textvergleich ohne Konfiguration: Screenshot aus Beyond Compare
Was sieht man?
Beispiel 1:
Hier wurde ein Interaktiver Report angepasst und als Primary abgespeichert.
Vergleicht man den vorherigen Export mit dem Export nach der Änderung, kann man erkennen, dass
die Spalte ADDRESS offensichtlich ausgeblendet wurde, da sie auf der rechten Seite fehlt (die
Variable rc1 wird als Parameter p_report_columns übergeben) und dass eine Sortierung angewendet
wurde (p_sort_column_1 und p_sort_direction_1):
Abb. 16: Interactive Report: Ausgeblendete Spalte und Sortierung
Nimmt man jetzt noch einen Filter hinzu, wird es richtig interessant, denn dann erscheint auf der
rechten Seite ein neues Objekt, die Filterbedingung (worksheet_condition vom Typ FILTER):
Abb. 17: Interactive Report: Filterbedingung
Man erkennt den Spaltennamen, auf den gefiltert wird (p_column_name =>'STATE'), den Operator
(p_operator =>'contains') und den Vergleichswert (p_expr =>'MO',). Außerdem wird sogar die daraus
generierte SQL-Bedingung angegeben
p_condition_sql =>'upper("STATE") like ''%''||upper(#APXWS_EXPR#)||''%'''
und man sieht, wie Apex die benutzerfreundliche Darstellung
realisiert. Der Ausdruck
p_condition_display =>'#APXWS_COL_NAME# #APXWS_OP_NAME# ''MO''
wird durch Substitution in der GUI zu
',
Beispiel 2:
In diesem Beispiel wurden Änderungen an einer Select-Liste vorgenommen.
Man sieht, dass das Item P6_CATEGORY wahrscheinlich verschoben wurde (kleinere Nummer in
p_item_sequence), was aber erst im Zusammenhang mit den anderen Sequence-IDs verifiziert werden
kann.
Des Weiteren wurde ein NULL-Wert erlaubt (p_lov_display_null=> 'YES'), und der Anzeige- und
Rückgabe-Wert für diesen definiert (p_lov_null_text und p_lov_null_value).
Ein Bedingung (p_display_when_type=>'CURRENT_PAGE_EQUALS_CONDITION') sorgt dafür,
dass das Item nur auf Seite 6 angezeigt wird (was bei einem Item auf Page 0 natürlich mehr Sinn
ergeben würde).
Im unteren Teil kann man sehen, dass zusätzlich noch Quick Picks angelegt wurden, und zwar zwei
Stück jeweils mit Label und Value (p_quick_pick_label_* und p_quick_pick_value_*).
Abb. 18: Diverse Änderungen an einem Page Item
Hier wurde der Typ eines Items von ‚Radio Group‘ auf ‚Select List‘ geändert. Außerdem wurde keine
zentral definierte LOV verwendet (erkennbar an der ID), sondern eine statisch. (p_lov=> 'STATIC2'):
Abb. 19: Änderung der LOV-Definition an einem Page Item
Probleme und Grenzen
Nicht immer gelingt es Beyond Compare, die beiden Dateien passend auszurichten. Dann kann man
dies manuell tun, indem man eine Zeile auf der linken Seite auswählt, [F7] drückt und dann mit der
Maus die Zeile auf der rechten Seite anklickt, die mit der linken Zeile ausgerichtet werden soll.
Die Apex-Export-Files sind gut strukturiert, wurden aber natürlich nicht mit dem primären Ziel erstellt, dass sie besonders gut zu vergleichen sein sollten.
Daher kommt es manchmal zu Effekten wie dem, das längerer SQL- und PL/SQL-Text unterschiedlich umgebrochen wird, wie in diesem Beispiel
Abb. 20:Ungleicher Umbruch eines langen Statements
Als erfahrener Entwickler wird man das relativ leicht erkennen können, aber man kann auch Abhilfe
schaffen, indem man nach Möglichkeit SQL in Views und PL/SQL in Packages auslagert, was auch
andere Vorteile hat.
Der Vergleich von Attributen – zum Beispiel von Items – ist relativ zuverlässig. Schwierig ist es
festzustellen, ob sich eine Template-Zuordnung geändert hat, da das Template nicht über einen
Namen, sondern über eine ID referenziert wird:
p_plug_template=> 12319517529116625534+ wwv_flow_api.g_id_offset,
Vergleichen wir zwei Versionen einer Applikation, die mit der gleichen Applikations-ID aus dem
gleichen Workspace exportiert wurden, sind die IDs der Objekte gleich, eine Änderung fällt also auf.
(Wenn die entsprechenden Zeilen eingeblendet sind.)
Handelt es sich allerdings um zwei Versionen mit unterschiedlichen Applikations-IDs oder aus unterschiedlichen Workspaces, sind die IDs aller Objekte unterschiedlich. Somit sind „echte“ Änderungen
einer Template-Zuordnung für uns nicht mehr erkennbar.
Fazit
Der Vergleich von Textdateien kann einem fast alle Unterschiede zwischen Applikationen zeigen; in
manchen Fällen muss man aber trotzdem im Application Builder nachsehen, was diese Unterschiede
bedeuten. Dafür werden einem sehr genau die Stellen gezeigt, an denen man suchen muss, was ein
großer Vorteil gegenüber den bisherigen Möglichkeiten innerhalb von Apex ist.
Beyond Compare bietet durch die Regulären Ausdrücke viele Möglichkeiten, sich den Vergleich
genauso zu konfigurieren, wie man ihn braucht.
Technisches
Die Beispiele stammen aus Apex 4.2.2 und 4.2.3.
Bei dem verwendeten Diff-Tool handelt es sich um Beyond Compare 3.3.8.
Kontaktadresse:
Sabine Heimsath
its-people GmbH
Lyoner Str. 44-48
D-60528 Frankfurt am Main
Telefon:
Fax:
E-Mail
Internet:
+49 (0) 69-247 521 00
+49 (0) 69-247 521 021
[email protected]
www. its-people.de
Herunterladen