Mehr als Metadaten: Das Application Express-Repository und wie man es nutzen kann

Application Express ist, das ist weithin bekannt, ein metadatengetriebenes Werkzeug. Das bedeutet, dass alle Details Ihrer APEX-Anwendungen in den Repository-Tabellen abgespeichert werden. Wenn Sie Ihre Applikation starten, liest Application Express die Definitionen aus und generiert die Webseiten Ihrer Anwendung.

Die Metadaten des Application Express-Repository sind Grundlage jeder Anwendung
Abbildung 1: Die Metadaten des Application Express-Repository sind Grundlage jeder Anwendung

Das Beste am APEX-Repository ist, dass es "einfach da" ist: Es muss nicht separat gepflegt werden; vielmehr bestehen APEX-Anwendungen aus nichts weiter als den Repository-Einträgen. APEX-Anwendungen dokumentieren sich im wahrsten Sinne des Wortes selbst; es kommt nun nur darauf an, diese Informationen sinnvoll zu nutzen. Dieser Tipp zeigt einige Ideen und Ansätze dazu auf. Direkten (lesenden) Zugriff auf das Repository erhalten Sie mit den APEX Data Dictionary Views, welche bereits vor einiger Zeit eingeführt wurden.

Der Blick ins Repository: APEX Dictionary Views

Welche Dictionary Views es überhaupt gibt, sagt Ihnen die View APEX_DICTIONARY; dort sind alle Views mit allen Spalten samt Erklärungen hinterlegt. Auch in der APEX Entwicklungsumgebung haben Sie Zugriff auf diese Views. Navigieren Sie dazu von der Startseite des APEX Workspace zu den Utilities (Werkzeuge) und dort zu den APEX Views (Abbildung 2).

Die APEX Dictionary Views sind hierarchisch organisiert
Abbildung 2: Die APEX Dictionary Views sind hierarchisch organisiert

An diesem Baum erkennen Sie, dass die Views hierarchisch organisiert sind; wenn Sie auf einen der Einträge im Baum klicken, sehen Sie die Spalten samt Erklärungen zum Inhalt (Abbildung 3).

Erklärungen zur View APEX_APPLICATIONS
Abbildung 3: Erklärungen zur View APEX_APPLICATIONS

Sie können die Views innerhalb der APEX Entwicklungsumgebung (SQL Workshop), aber auch außerhalb von Application Express (SQL*Plus, JDBC-Verbindungen) nutzen. Wenn Sie sich per SQL*Plus verbinden, gelten dennoch die Sicherheitsregeln von Application Express: So zeigt die folgende Abfrage, die mit SQL*Plus durchgeführt wird, nur den Workspace, dem das Schema des angemeldeten Users zugewiesen wurde - die anderen Workspaces der Instanz werden ausgeblendet.

SQL> select workspace_id, workspace, applications from apex_workspaces

       WORKSPACE_ID WORKSPACE                      APPLICATIONS
------------------- ------------------------------ ------------
   1841600194840592 PARTNER                                  14

1 Zeile wurde ausgewählt.

Mit der View APEX_APPLICATIONS kann man sich nun die einzelnen Anwendungen ansehen; nachgelagerte Views enthalten Details zu den Anwendungskomponenten. Mit der START WITH ... CONNECT BY-Syntax kann die Hierarchie auch in SQL*Plus dargestellt werden.

with apex_dict_tables as (
  select apex_view_name, parent_view
  from apex_dictionary 
  where comment_type='View'
)
select lpad(' ',level * 2) || '|' ||apex_view_name view_name
from apex_dict_tables
start with apex_view_name = 'APEX_APPLICATIONS' 
connect by (parent_view = prior apex_view_name and level < 3)
/

VIEW_NAME
---------------------------------------------------
  | APEX_APPLICATIONS
    | APEX_APPLICATION_ALL_AUTH
    | APEX_APPLICATION_AUTH
    | APEX_APPLICATION_AUTHORIZATION
    | APEX_APPLICATION_BC_ENTRIES
    | APEX_APPLICATION_BREADCRUMBS
    | APEX_APPLICATION_BUILD_OPTIONS
    | APEX_APPLICATION_CACHING
    | APEX_APPLICATION_GROUPS
    | :

Informationen über eine Anwendung: Aus dem Repository!

Eine erste Idee wäre nun, sich diverse Informationen über eine APEX-Anwendung direkt aus dem Repository zu holen. Ein Beispiel dafür ist der Page Flow-Generator von Patrick Wolf, welcher die Seitenverzweigungen in einer APEX-Applikation grafisch darstellt.

Denkt man diese Idee weiter, so könnte man das Repository auch zur Analyse bestehender APEX-Applikationen zu nutzen. Das Ziel wäre, die Informationen als Grundlage zur Analyse und Bewertung vorhandener oder zur Kalkulation neuer Anwendungen zu nutzen. Im folgenden ist eine mögliche Vorgehensweise hierzu beschrieben. Allerdings skizziert dieser Tipp nur die grundsätzliche Vorgehensweise: Die hier aufgeführten Schritte sind nicht vollständig - für eine komplette Lösung müsste man wesentlich mehr Information aus dem Repository auslesen und verarbeiten.

In einem ersten Schritt verschaffen wir uns einen Überblick über die Struktur einer Anwendung. Die relevanten Informationen sind allerdings über mehrere APEX Dictionary Views verteilt, so dass man sie zunächst zusammenfassen muss. Die folgende (etwas längere) SQL-Anweisung tut dies für Anwendungsseiten, Regionen, Seitenelemente und Prozesse. Die zusammengefasste Information wird als View APEX_APP_STRUCTURE bereitgestellt. Spielen Sie das Skript mit dem SQL Workshop oder mit SQL*Plus ein.

create or replace view apex_app_structure as
/* 
 * Ebene 0: Applikationen 
 */
select 
  application_id            app_id,
  'APPLICATION'             comp_id, 
  to_char(application_id)   comp_name,
  null                      parent_comp_id, 
  'APPLICATION'             comp_type,
  null                      comp_subtype,
  null                      comp_source
from apex_applications union all (
/* 
 * Ebene 1: a) Seiten
 */
 select 
   p.application_id         app_id,
   'PAGE_' || page_id       comp_id,
   to_char(page_id)         comp_name,
   'APPLICATION'            parent_comp_id,
   'PAGE'                   comp_type,
   null                     comp_subtype,
   null                     comp_source
 from apex_application_pages p, apex_applications a 
 where p.application_id = a.application_id
) union all (
/* 
 * Ebene 2: a) Regionen
 */
 select 
   r.application_id                         app_id,
   'REG_P' || page_id || '_'|| region_name  comp_id,
   region_name                              comp_name,
   'PAGE_' || page_id                       parent_comp_id,
   'REGION'                                 comp_type,
   source_type                              comp_subtype,
   null                                     comp_source
 from apex_application_page_regions r, apex_applications a 
 where a.application_id = r.application_id
) union all (
/* 
 * Ebene 2: b) Seitenelemente
 */
 select 
    r.application_id                        app_id,
   'ITEM_P' || page_id || '_'|| item_name   comp_id,
   item_name                                comp_name,
   'PAGE_' || page_id                       parent_comp_id,
   'ITEM'                                   comp_type,
   display_as                               comp_subtype,
   item_source_type                         comp_source
 from apex_application_page_items r, apex_applications a 
 where a.application_id = r.application_id
) union all (
/* 
 * Ebene 2: c) Seitenprozesse
 */
 select 
    r.application_id                        app_id,
   'PROC_' || page_id || '_'|| process_name comp_id,
   process_name                             comp_name,
   'PAGE_' || page_id                       parent_comp_id,
   'PROCESS'                                comp_type,
   process_type                             comp_subtype,
   null                                     comp_source
 from apex_application_page_proc r, apex_applications a 
 where a.application_id = r.application_id
)
/

Selektiert man die View, so erhält man "auf einen Blick" einen Überblick über eine Applikation.

with myapp as (
  select * from apex_app_structure where app_id={Hier Applikations-ID einsetzen}
) 
select lpad(' ',(level-1) * 2) || comp_type ||': '||comp_name || ' ('||comp_subtype||')' component
from myapp
start with comp_id='APPLICATION'
connect by parent_comp_id = prior comp_id
  order siblings by app_id, comp_type
/

COMPONENT
------------------------------------------------------------------------------------

APPLICATION: 103 ()
  PAGE: 2 ()
    REGION: Abteilungs-Daten ()
    REGION: Gehaltsaufteilung ()
  PAGE: 101 ()
    ITEM: P101_USERNAME (Text Field)
    ITEM: P101_PASSWORD (Password (submits when Enter pressed, saves state))
    PROCESS: Login (PL/SQL anonymous block)
    PROCESS: Set Username Cookie (PL/SQL anonymous block)
    PROCESS: Get Username Cookie (PL/SQL anonymous block)
    PROCESS: Clear (Clear Cache for all Items on Pages (PageID,PageID,PageID))
    REGION: Anmelden ()
  PAGE: 1 ()
    ITEM: P1_DEPTNO (Select List with Submit)
    REGION: Abteilungen ()
  PAGE: 3 ()
    ITEM: P3_EMPNO (Text Field)
    ITEM: P3_ENAME (Text Field)
    ITEM: P3_JOB (Text Field)
    ITEM: P3_MGR (Select List)
    ITEM: P3_HIREDATE (Date Picker (DD/MM/YYYY))
    ITEM: P3_SAL (Text Field)
    ITEM: P3_COMM (Text Field)
    :

Der nächste Schritt ist dann das Auszählen der verschiedenen Komponenten:

select 
  comp_type,
  comp_subtype,
  count(comp_name) anzahl
from apex_app_structure
where app_id = 103
group by rollup (comp_type, comp_subtype)

COMP_TYPE       COMP_SUBTYPE                                                     ANZAHL
--------------- ------------------------------------------------------------ ----------
ITEM            Text Field                                                            6
ITEM            Select List                                                           2
ITEM            Select List with Submit                                               1
ITEM            Date Picker (DD/MM/YYYY)                                              1
ITEM            Password (submits when Enter pressed, saves state)                    1
ITEM            -ALLE-                                                               11
REGION          Report                                                                2
REGION          HTML/Text                                                             2
REGION          SVG Chart                                                             1
REGION          -ALLE-                                                                5
PROCESS         Automated Row Fetch                                                   1
PROCESS         PL/SQL anonymous block                                                3
:               :                                                                     :

Diese Ausgabe zeigt sehr gut, dass die "Rohdaten" des Repository für eine Analyse und Bewertung der Anwendung nicht besonders gut geeignet sind. So müsste man die Elementtypen Select List und Select List With Submit entsprechend zusammenfassen. Ein Ansatz dafür wäre, die Zuordnungen in einer eigenen Tabelle abzulegen.

create table item_type_structure(
  apex_item_type   varchar2(200),
  component_type   varchar2(200)
)
/

insert into item_type_structure values ('Text Field', 'SIMPLE_TEXT');
insert into item_type_structure values ('Date Picker (DD/MM/YYYY)', 'SIMPLE_TEXT');
insert into item_type_structure values ('Select List', 'LOV_BASED');
insert into item_type_structure values ('Select List With Submit', 'LOV_BASED');
insert into item_type_structure values ('Hidden', 'SIMPLE_VARIABLE');

Bindet man diese Tabelle in obige View ein, könnte das Ergebnis des "Auszählens" wie folgt aussehen:

COMP_TYPE       COMP_SUBTYPE                                                     ANZAHL
--------------- ------------------------------------------------------------ ----------
ITEM            SIMPLE_TEXT                                                           8
ITEM            LOV_BASED                                                             3
ITEM            -ALLE-                                                               11
REGION          SQL BASED                                                             3
REGION          HTML/Text                                                             2
REGION          -ALLE-                                                                5
PROCESS         Automated Row Fetch                                                   1
PROCESS         PL/SQL anonymous block                                                3
:               :                                                                     :

Um eine möglichst realitätsnahe Abbildung zu erreichen, fehlen allerdings noch einige APEX-Komponenten. Die View zum "Sammeln" der Informationen aus dem Repository müsste also noch erweitert werden.

  • Validierungen (APEX_APPLICATION_PAGE_VAL)
  • Berechnungen (Computations) in der View APEX_APPLICATION_PAGE_COMP
  • Zu jeder Komponente gibt es u.U. Bedingungen (Spalte CONDITION_TYPE in den Views)
  • Applikationsprozesse und -Elemente (APEX_APPLICATION_PROCESSES und APEX_APPLICATION_ITEMS).

Hat man alle Informationen in aufbereiteter Form beisammen, so gilt es, die verschiedenen Komponenten zu gewichten. Das Ziel ist wiederum, die verschiedenen Komponenten vergleichbar zu machen. Für die Elemente (Items) wären beispielsweise denkbar:

  • Textfeld (SIMPLE_TEXT): Faktor 1
  • Versteckt (SIMPLE_VARIABLE): Faktor 0,25
  • Auf LOV basierend (LOV_BASED): Faktor 2

Für Berichte könnte folgendes gelten:

  • Interaktive Berichte auf eine Tabelle oder View: Faktor 1
  • Klassische Berichte auf eine Tabelle oder View: Faktor 3
  • Interaktive SQL-Berichte: Faktor 4
  • Klassische SQL-Berichte: Faktor 6
  • Berichte mit dynamischem SQL: Faktor 10

Es gilt also, für alle Komponenten zunächst eine Zuordnung in wenige, gut voneinander abgegrenzte Klassen vorzunehmen und diese anschließend zu gewichten. Am Ende erhält man wenige Kennzahlen, die Auskunft über die Komplexität einer Anwendung geben. Wenn es Aufzeichnungen über die konkrete Entwicklungszeit gibt (Personentage), so hat man einen Zusammenhang zwischen Anwendung und Aufwand, der anhand anderer Anwendungen verifiziert und als Grundlage für die Kalkulation neuer Projekte dienen kann.

Natürlich wird ein solches Verfahren keine exakten Voraussagen machen; gerade während der Entwicklung neuer Anwendungen ändern sich die Anforderungen ständig. Ein geübter APEX Entwickler kann so allerdings schon während der Diskussion der Anforderungen abschätzen, welche Element- oder Berichtstypen wahrscheinlich benötigt werden. Damit können die voraussichtlich benötigten Komponenten schon im Vorfeld abgeschätzt, gewichtet und aufsummiert werden und man erhält eine (wenn auch sehr grobe) Abschätzung über den voraussichtlichen Aufwand. Abbildung 4 macht diesen Prozeß deutlich.

Analyse und Schätzung von APEX-Anwendungen
Abbildung 4: Analyse und Schätzung von APEX-Anwendungen

Zusammenfassung und Ausblick

Da Application Express ein metadatengetriebenes Werkzeug ist, dokumentieren sich alle Anwendungen automatisch selbst. Aufbau und Komponenten einer Anwendung liegen stets in strukturierter Form vor; man muss keine Code-Analyse durchführen, um bspw. festzustellen, wieviele Eingabefelder in einem Formular vorkommen. Dieser Tipp zeigt einen Ansatz auf, wie man die Repository-Informationen nutzen könnte, um vorhandene Anwendungen zu analysieren. Es ist einleuchtend, dass eine konkrete Umsetzung sehr unternehmens- bzw. projektgruppenspezifisch wäre, daher wurde auf detailliertere Ausführungen verzichtet.

Darüber hinaus können die Repository-Tabellen noch für weitere Zwecke genutzt werden: Ein Beispiel wäre die Qualitätsicherung von APEX-Anwendungen. Hierfür existiert mit dem APEX Advisor (als Teil der APEX Essentials) von Patrick Wolf bereits ein fertiges Werkzeug; mehr Details hierfür werden in einem späteren Tipp der Community erscheinen. Ein anderes Beispiel ist das Entwickeln von generischem Code; die APEX Community enthält im Bereich AJAX einen Tipp (u.a. Cascading LOV), der diesem Grundsatz verfolgt.

Zurück zur Community-Seite