PL/SQL Gateway


Spis treści


1 Wstęp

Oracle Internet Application Server (iAS) zawiera:

PL/SQL Gateway pozwala na zbudowanie aplikacji internetowej bazującej na języku PL/SQL. Kod aplikacji przechowywany jest jako procedury (stored procedures) które mogą pobierać dane z bazy danych i generować na ich podstawie dynamiczne strony www.

1.1 Środowisko stanowe vs. środowisko bezstanowe

W środowisku bezstanowym każde zapytanie HTTP od klienta jest obsługiwane za pomocą odrębnej sesji z bazą danych. Jeżeli chcemy przechowywać stan aplikacji to musimy samodzielnie zapewnić jego przechowywanie za pomocą ciasteczek lub też oddzielnej tabeli w bazie danych. Stan transakcji nie jest zapamiętywany. Jeżeli procedura zakończyła się sukcesem to jest wykonywana instrukcja COMMIT, w przeciwnym razie jest wykonywana instrukcja ROLLBACK.

W środowisku stanowym każde zapytanie HTTP od klienta jest obsługiwane za pomocą tej samej sesji z bazą danych. Stan aplikacji może być przechowywany w zmiennych zdefiniowanych w pakiecie PL/SQL. Transakcje mogą rozciągać się na kilka zapytań, ponieważ nie są wykonywane "ukryte" instrukcje COMMIT i ROLLBACK.

Oracle Internet Application Server może być skonfigurowany do pracy zarówno w środowisku stanowym jak i w środowisku bezstanowym.

1.2 Deskryptory DAD (Database Access Descriptor)

Każde zapytanie kierowane do bramki PL/SQL zawiera deskryptor DAD. Deskryptory te są tworzone przez administratora. Każdy taki deskryptor ma przypisany zbiór parametrów konfiguracyjnych używanych przy łączeniu się z bazą danych. Można w nim także podać nazwę użytkownika i hasło; jeżeli nie są podane to użytkownik będzie autoryzowany podczas połączenia z bramką.

1.3 Schemat działania bramki PL/SQL

Kiedy serwer otrzyma zapytanie od klienta wykonywane są następujące czynności:

  1. Serwer WWW otrzymuje zapytanie od klienta i przekierowuje je do bramki PL/SQL.
  2. Bramka PL/SQL na podstawie deskryptora DAD ustala parametry połączenia z bazą danych.
  3. Bramka PL/SQL łączy się z bazą danych i wywołuje procedurę PL/SQL.
  4. Procedura PL/SQL generuje stronę HTML zawierającą dynamiczne dane pobrane przez nią z bazy danych.
  5. Wygenerowana strona jest przekierowana z powrotem do bramki PL/SQL, a następnie do klienta.

1.4 Schemat adresowania dla PL/SQL Gateway

Aby wywołać procedurę PL/SQL poprzez PL/SQL Gateway, należy użyć adresu URL o następującym schemacie:

protokół://nazwa_hosta[:port]/prefiks/DAD/[[!][schemat.][pakiet.]nazwa_procedury[?parametry]]

gdzie:

protokół to nazwa protokołu (http lub https).

nazwa_hosta jest nazwą komputera z serwerem WWW.

port to port na którym nasłuchuje serwer WWW (domyślnie 80)

prefix to wirtualna ścieżka skonfigurowana do przechwytywania zapytań do bramki PL/SQL (zazwyczaj używa się nazwy pls)

DAD to nazwa deskryptora DAD

! oznacza że używamy schematu przekazywania parametrów "flexible parameter passing"

schemat to nazwa schematu bazy danych

pakiet to nazwa pakietu zawierającego wywoływaną procedurę (jeżeli procedura należy do pewnego pakietu)

nazwa_procedury jest nazwą procedury PL/SQL którą chcemy wywołać. To musi być procedura - wywołania funkcji nie są dozwolone. Procedura powinna mieć wyłącznie parametry IN.

?parametry to parametry przekazywane do wywoływanej procedury (przy przekazywaniu parametrów z formularzy HTMLowych można także używać metody POST)

1.5 Opis pakietów wchodzących w skład PL/SQL Web Toolkit

PL/SQL Web Toolkit zawiera następujące pakiety:

Pakiet

Opis 

htf oraz htp

Pakiet htp (hypertext procedures) zawiera procedury generujące znaczniki języka HTML, np. procedura htp.anchor generuje znacznik <A>.

Pakiet htf (hypertext functions) jest bardzo podobny do htp. Jedyną różnicą jest to że zamiast procedur występują tutaj funkcje. Funkcje te nie wysyłają kodu HTML do generowanej strony www, ale zwracają go jako wynik (varchar2), który możemy później wypisać za pomocą procedury htp.print.

owa_sec

Zawiera procedury pozwalające na autoryzację użytkowników.

owa_util

Zawiera "użyteczeczne" procedury np. zwracanie wartości zmiennych środowiskowych CGI czy wypisanie całej tabeli.

owa_pattern

Pozwala na operowanie na wyrażeniach regularnych.

owa_text

Pozwala operować na blokach tekstowych, używane przez owa_pattern.

owa_image

Wspomaga operowanie obrazkami.

owa_cookie

Pozwala generować i odczytywać ciasteczka.

owa_opt_lock

Zapewnia procedury do blokowania dostępu do danych.

owa_cache

Zawiera procedury i funkcje pozwalające na kontrolę cache'owania w celu zwiększenia wydajności.

1.6 Prosty przykład użycia PL/SQL Gateway

Uruchamiamy monitor PL/SQL (sqlplus), logujemy się jako scott/tiger i tworzymy prostą procedurkę:

create or replace procedure current_users AS
    ignore boolean;
  BEGIN
    htp.htmlopen;
    htp.headopen;
    htp.title('Current Users');
    htp.headclose;
    htp.bodyopen;
    htp.header(1, 'Current Users');
    ignore := owa_util.tablePrint('all_users');
    htp.bodyclose;
    htp.htmlclose;
  END;
  /
Następnie (jako administrator) tworzymy odpowiedni deskryptor DAD, który pozwoli na uzyskanie dostępu do naszej procedury z poziomu bramki PL/SQL. Aby uruchomić naszą procedurę wpisujemy w przeglądarce adres
http://<host>:<port>//pls/nazwa_DAD/scott.current_users

1.7 Przekazywanie parametrów

1.7.1 Przekazywanie parametrów przez nazwę

Standardową metodą przekazywania parametrów do procedury PL/SQL jest przekazywanie przez nazwę. Każda zmienna przekazana przez ciąg ?parametry zostaje przekazana jako parametr o odpowiedniej nazwie. Kolejność nie jest istotna, ponieważ parametry są identyfikowane na podstawie unikatowej nazwy.

Możliwe jest przekazywanie parametrów typu VARCHAR2 oraz NUMBER (być może także innych typów prostych - dokumentacja milczy na ten temat, a nie udało mi się sprawdzić empirycznie gdyż bramka nie jest jeszcze zainstalowana). Istnieje także możliwość przekazania do procedury parametru typu tablicowego (w przykładach podanych w dokumentacji używany jest typ owa_util.ident_arr, ale prawdopodobnie możliwe jest użycie także innych typów). Dzięki temu możliwe staje się np. submitowanie formularzy zawierających pola typu <SELECT MULTIPLE>.

Procedura, którą chcemy wywoływać za pomocą bramki PL/SQL powinna posiadać parametry wyłącznie typu IN.

Przykład 1: Definiujemy procedurę:

PROCEDURE my_proc (val IN VARCHAR2);

Procedurę taką możemy wykonać za pomocą następującego URLa

http://www.acme.com/pls/myDAD/my_proc?val=john

Przykład 2: Definiujemy procedurę zawierającą parametr typu tablicowego:

PROCEDURE my_proc (val IN owa_util.ident_arr); -- parametr typu tablicowego

Procedurę taką możemy wykonać za pomocą następującego URLa

http://www.acme.com/pls/myDAD/my_proc?val=john&val=sally

Uwaga: Przy wywoływaniu procedury posiadającej parametr typu tablicowego nie należy pomiędzy kolejnymi jej wartościami wstawić innego parametru, ponieważ spowoduje to błąd. Przykład błędnego wywołania:

http://www.acme.com/pls/myDAD/my_proc?val=john&x=1&val=sally

Tego typu wywołania można obsługiwać poprawnie jedynie za pomocą schematu "flexible parameter passing".

1.7.2 Przeciążanie procedur wywoływanych przez PL/SQL Gateway

Jak wiadomo z BD lab, język PL/SQL daje możliwość przeciążania. Ta własność PL/SQL'a jest również możliwa do wykorzystania w aplikacjach wykorzystujących bramkę PL/SQL. Należy jednak pamiętać o pewnych ograniczeniach spowodowanych przez konieczność typowania parametrów otrzymanych z URLa. Parametry mające różne typy powinny mieć różne nazwy, tak aby możliwa była identyfikacja.

Przykład 1: Ta procedura jest poprawna dla kompilatora PL/SQL ale nie będzie poprawnie działać przy wykorzystaniu bramki PL/SQL.

CREATE PACKAGE my_pkg AS
  PROCEDURE my_proc (val IN VARCHAR2);
  PROCEDURE my_proc (val IN NUMBER);
END my_pkg;

Przykład 2: Ta procedura jest poprawna dla kompilatora PL/SQL i działa z bramką PL/SQL.

CREATE PACKAGE my_pkg AS
  PROCEDURE my_proc (val_var2 IN VARCHAR2);
  PROCEDURE my_proc (val_num IN NUMBER);
END my_pkg;

Możliwe jest jednak zdefiniowanie dwóch wersji procedury mającej jeden parametr o identycznej nazwie, jeżeli pierwsza wersja używa typu prostego natomiast druga - typu tablicowego. Pierwsza wersja procedury jest uruchamiana wtedy, gdy tablica ma długość 1.

Przykład 3: Definiujemy dwie wersje tej samej procedury:

CREATE PACKAGE my_pkg AS
  PROCEDURE my_proc (val IN VARCHAR2);  --parametr typu prostego
  PROCEDURE my_proc (val IN owa_util.ident_arr);  --parametr typu tablicowego
END my_pkg;

Jeżeli wywołamy procedurę za pomocą następującego URLa

http://www.acme.com/pls/myDAD/my_proc?val=john

to uruchomiona zostanie pierwsza wersja procedury. Natomiast wywołanie za pomocą URLa

http://www.acme.com/pls/myDAD/my_proc?val=john&val=sally

spowoduje uruchomionie drugiej wersja procedury.

1.7.3 Przekazywanie parametrów za pomocą schematu "flexible parameter passing"

Oprócz standardowego przekazywania parametrów możliwe jest też użycie schematu "flexible parameter passing". Polega to na tym, że zamiast przekazywać do procedury kolejne elementy ciągu ?parametry jako kolejne parametry, przekazywane są tablice nazw i wartości parametrów. Jest to szczególnie użyteczne jeżeli przekazujemy dane z formularza zawierającego pola <SELECT MULTIPLE> (choć możliwe jest także "klasyczne" przekazanie parametru przez nazwę - przekazujemy parametr typu tablicowego) oraz w przypadku formularza posiadającego wiele pól o tej samej nazwie. W takim przypadku niemożliwe jest skonstruowanie procedury używającej "klasycznego" przekazywania parametrów, ponieważ URL zawiera dane w formacie ...&nazwa=wartość1&nazwa_inna=wartość_inna&nazwa=wartość2&... Schemat "flexible parameter passing" ma też zastosowanie gdy istnieje bardzo wiele pól formularza a nie chcemy pisać brzydkiego kodu zawierającego kilometrową listę parametrów.

Jeżeli chcemy aby procedura była wywoływana przy użyciu schematu "flexible parameter passing" należy w URLu umieścić prefiks "!" przed nazwą procedury. Procedura która będzie wołana przy użyciu schematu fpp powinna mieć jeden z następujących formatów:


2 Buforowanie

Aby zwiększyć wydajność aplikacji internetowych wykorzystujących bramkę PL/SQL można wykorzystać możliwość buforowania oferowaną przez PL/SQL Gateway. Pozwalają one na buforowanie stron WWW utworzonych przez procedury. Kolejne żądania odczytu tej samej strony są obsługiwane za pomocą cache'u, przy czym możliwe jest sprawdzenie w bazie danych czy strona się nie zdezaktualizowała. W ten sposób zmniejsza się obciążenie serwera DBMS oraz zwiększa się wydajność i skalowalność naszej aplikacji.

2.1 Mechanizmy buforowania udostępniane przez protokół HTTP

Istnieje kilka różnych mechanizmów buforowania udostępnianych przez protokół HTTP. Protokół ten określa format zapytania kierowanego do serwera (request) oraz format odpowiedzi zwróconej przez serwer (response). Przeglądarka WWW może przesyłać do klienta metadane w Request Headers, natomiast programy/skrypty generujące strony WWW mogą zwracać metadane w Response Headers. Dzięki takiemu schematowi komunikacji możliwe jest buforowanie - stwierdzenie czy strona jest aktualna odbywa się poprzez przesyłanie odpowiednich metadanych.

2.2 Buforowanie z walidacją (validation technique)

Podczas generowania strony WWW tworzony jest Response Header "last-modified". Determinuje on czas utworzenia strony WWW na serwerze. Przeglądarka WWW obsługująca buforowanie przechowuje tę informację wraz z zawartością strony. Kiedy pojawiają się kolejne żądania do tej samej strony (identyczny URL) przeglądarka WWW wykonuje następujące czynności:

Serwer WWW po otrzymaniu żądania zawierającego nagłówek "if-modified-since" sprawdza czy strona jest aktualna. Jeżeli tak, to generowana jest odpowiedź "HTTP/1.1 304 Not Modified". Jeżeli nie to do klienta przesyłana jest nowa wersja strony z aktualnym nagłówkiem "last-modified".

Inną wersją buforowania z walidacją są nagłówki "ETag" (Entity Tag). Wartością tego nagłówka jest string zawierający informację o ważności strony. Wartość ETag zależy od typu aplikacji generującej daną stronę WWW. Jest to metoda bardziej ogólna od "if-modified-since", która pozwala przesyłać jedynie wartości będące datą. Mechanizm komunikacji przeglądarki WWW z serwerem jest identyczny, zmieniają się tylko nazwy nagłówków ("last-modified" -> "ETag"; "if-modified-since" -> "if-match").

2.2 Buforowanie z wygasaniem (expires technique)

Podczas generowania strony WWW tworzony jest Response Header "expires", zawierający informację o tym jak długo strona wygenerowana przez serwer jest aktualna. W tym okresie przeglądarka WWW nie musi kontaktować się z serwerem - strona przechowywana w buforze na pewno jest aktualna. Podczas żądania odwołującego się do tej samej strony zawartość jest więc ściągana z bufora.

2.3 Buforowanie na poziomie użytkownika vs. buforowanie na poziomie systemu

Procedura PL/SQL generująca stronę WWW może podać poziom buforowania: "USER" dla buforowania na poziomie użytkownika lub "SYSTEM" dla buforowania na poziomie całego systemu. To pozwala systemowi buforowania zintegrowanemu w PL/SQL Gateway na przechowywanie mniejszej ilości redundantnych plików jeżeli więcej niż jeden użytkownik pobiera tę samą stronę.

Różnica pomiędzy buforowaniem na poziomie użytkownika a buforowania na poziomie systemu polega na wykorzystaniu informacji o użytkowniku. W przypadku buforowania na poziomie systemu informacja o użytkowniku nie jest przechowywana w systemie i bufor może być wykorzystywany przez wielu użytkowników. Natomiast w przypadku buforowania na poziomie użytkownika kluczem dla systemu buforowania jest nie tylko URL, ale także informacja o użytkowniku. Jeżeli wielu użytkowników pobiera tę samą stronę, to dla każdego z nich tworzony jest osobny wpis w systemie buforowania.

Buforowanie na poziomie użytkownika jest potrzebne w przypadku generowania stron zawierających specyficzne informacje na temat zalogowanego użytkownika (np. koszyk z przedmiotami, na które chcemy się zarejestrować). W takich przypadkach sam URL jako klucz buforowania jest niewystarczający. Natomiast w przypadku ogólnych informacji dostępnych dla wszystkich użytkowników (np. lista wszystkich przedmiotów) wystarcza buforowanie na poziomie systemu.

Jeżeli kopia strony znajduje się zarówno w buforze dla całego systemu jak i w buforze danego użytkownika, wykorzystywana jest ta z poziomu użytkownika.

2.4 Pakiet owa_cache

Pakiet owa_cache zawiera procedury i funckcje pozwalające na implementację buforowania naszej aplikacji bazodanowej. Pozwala on na skonfigurowanie buforowanie oraz pobieranie nagłówków (cache headers). Dzięki temu pakietowi wykorzystanie buforowania zintegrowanego w PL/SQL Gateway jest szybkie i proste.

Funkcje udostępniane przez pakiet:


3 Optymistyczne strategie blokowania.

Aplikacje działające w Internecie pozwalają na jednoczesny dostęp wielu użytkownikom. Jeżeli wielu użytkowników próbuje zmodyfikować ten sam rekord to nie wiadomo jaki otrzymamy wynik.

Pakiet owa_opt_lock pozwala na stosowanie optymistycznych strategii blokowania. Odczytywane przez klienta zasoby nie są blokowane, natomiast przed zapisaniem nowych wartości do bazy danych następuje weryfikacja, czy "stare" (tzn. te sprzed modyfikacji) wartości nie uległy zmianie. Jest to metoda optymistyczna - nie pozwala na ochronę rekordu przed modyfikacjami dokonywanymi przez innych użytkowników, a jedynie na odrzucenie bieżących zmian jeżeli w międzyczasie rekord został zmodyfikowany. Użycie konwencjonalnego (pesymistycznego) blokowania nie jest możliwe, ponieważ HTTP jest protokołem bezpołączeniowym.

Aby możliwe było sprawdzenie "starych" wartości rekordu, są one przechowywane na generowanej stronie WWW jako ukryte pola. Możliwe jest także przechowywanie sumy kontrolnej (checksum) zamiast wartości wszystkich pól.

Zarówno przechowywanie bieżących wartości w kodzie generowanej strony jak i sprawdzenie, czy wartości nie uległy zmianie musi być wykonane jawnie w kodzie procedury (kontrola poprawności nie jest generowana automatycznie). Pakiet owa_opt_lock jest więc raczej pewnego rodzaju usprawnieniem pozwalającym na kontrolę poprawności przeprowadzanej modyfikacji niż rzeczywistym blokowaniem.

3.1 Funkcje udostępniane przez pakiet owa_opt_lock

Pakiet owa_opt_lock udostępnia następujące funckje:

3.2 Sposób użycia funkcji store_values i verify_values

Kod przedstawiony poniżej obrazuje użycie funkcji store_values i verify_values. W podobny sposób można wykorzystać funkcje checksum, tyle że wtedy trzeba samodzielnie generować ukryte formularze do kodu (checksum tylko oblicza wartość).

procedure selectToEdit is
  v_user varchar2(30);
  v_rowid varchar2(20);
begin
  ...
  select ROWID, USER
  into v_rowid, v_user
  from tableName
  where ...
  ...
  owa_opt_lock.store_values(v_user, 'tableName', v_rowid);
end;

procedure doUpdate (old_tableName in owa_opt_lock.vcArray) is
begin
  ...
  if (owa_opt_lock.verify_values(old_tableName) = false) then
    -- Rekord został zmodyfikowany - możemy to obsłużyć
  else
    -- Wszystko OK - można modyfikować rekord
  end if;
  ...
end;

4 Inne pakiety wchodzące w skład Oracle Web Toolkit

Ten rozdział skrótowo opisuje pozostałe pakiety wchodzące w skład Oracle Web Toolkit. Pełna specyfikacja opisywanych pakietów znajduje się w dokumentacji Oracle Web Toolkit (Oracle Web Toolkit Reference), która znajduje się na serwerze firmy Oracle (na rainbow chyba nie ma, a przynajmniej mi nie udało się znaleźć).

4.1 owa_cookie

Ten pakiet pozwala na zarządzanie ciasteczkami. Pozwala na tworzenie i sprawdzanie ich wartości. Udostępniane funkcje:

4.2 owa_pattern

Ten pakiet pozwala na operowanie na wyrażeniach regularnych. Nie jest to pakiet o możliwościach takich jak np. Perl, ale pozwala na wykonywanie podstawowych operacji - wyszukiwania i zamiany fragmentów tekstu. Ze względu na specyfikę aplikacji internetowych udogodnienia oferowane przez pakiet są bardzo przydatne. Udostępniane funkcje:

4.3 owa_image

Ten pakiet zawiera funkcje pozwalające na pobranie współrzędnych punktu, na który kliknął użytkownik. Jest to używane w przypadku istnienia na stronie WWW tzw. image maps, które wywołują procedury poprzez bramkę PL/SQL. Udostępniane funkcje to get_x i get_y. Przykładowy program:

create or replace procedure process_image
    (my_img in owa_image.point)
    x integer := owa_image.get_x(my_img);
    y integer := owa_image.get_y(my_img);
begin 
    /* przetwórz współrzędne */
end

4.4 owa_util

Ten pakiet zawiera wiele "użytecznych" funkcji wspomagających obsługę CGI, HTTP, bazy danych oraz kalendarza. Najbardziej użyteczne funkcje znajdujące się w tym pakiecie to:

4.5 utl_file

Ten pakiet właściwie nie należy do Oracle Web Toolkit (jest standardowym pakietem instalowanym razem z PL/SQL'em), ale warto o nim wspomnieć gdyż dostarcza funkcje pozwalające operować na plikach - może się więc przydać np. wtedy gdy chcemy zrobić prosty licznik odwiedzin. Udostępniane funkcje to np. fopen, get_line i fclose (nazwy są takie jak w stdio.h). Przykładowy program:

procedure wypiszplik is
  v_file utl_file.file_type;
  v_line varchar2(80);
begin
  v_file = utl_file.fopen('katalog', 'plik', flagi);
  loop
    utl_file.get_line(v_file, v_line);
    htp.p(v_line);
  end loop;
  fclose(v_file);
end;

4.6 owa_sec

Ten pakiet pozwala na zmianę metody autoryzacji projektowanej aplikacji. Ustawienie autoryzacji odbywa cię przez wywołanie funkcji owa_sec.set_authorization. Można też ustalić sposób autoryzacji przy tworzeniu deskryptora DAD.

Funkcję autoryzującą możemy napisać samemu. Musi ona nazywać się authorize i zwracać wartość typu boolean.

functiun authorize return boolean is
begin
  ...
end;

W tej funkcji można korzystać ze zmiennych owa_sec.get_user_id i owa_sec.get_password, które oznaczają wczytane od użytkownika wartości. Należy także pamiętać, że aby używać własnych funkcji autoryzacji należy właściwie ustawić DAD oraz umieścić funkcję we właściwym pakiecie (zawierającym aplikację lub też owa_custom, w zależności od wybranej metody autoryzacji).


Dodatki (źródło: zeszłoroczny referat)

Dodatek 1. Spis procedur i funkcji pakietów htp i htf

Dodatek 2. Spis znaczników dla funkcji z pakietu owa_pattern