Web Serwisy SOAP i serwer aplikacji PAS cz.II

W poprzedniej części wygenerowaliśmy 2 pliki r-kodów, które chcemy wystawić jako web serwisy SOAP. Mamy także wygenerowane przy pomocy narzędzia Proxy Generator pliki CustOrder.wsm oraz CustOrder.wsdl.

Plik .wsdl posłuży nam do wygenerowania dokumentacji o zdefiniowanym web serwisie. W oknie komend wpisujemy polecenie:

bprowsdldoc custorder.wsdl ./output

W podkatalogu output znajdziemy nowe pliki .html, zawierające informacje o zdefiniowanych procedurach, funkcjach itp. Są także przykłady kodu do przyłączenia i uruchomienia wybranej metody. Poniżej znajdują się definicje dwóch procedur z listą parametrów. Proszę zauważyć, że został automatycznie dodany parametr result.

Aby wdrożyć nowy web serwis musimy mieć instancję serwera PASOE. Posłużę sie tutaj tą samą instancją z przyłączoną bazą sports2000, jak opisałem w artykule o serwisach REST.

Kopiuję 2 r-kody do podkatalogu openedge w ścieżce PROPATH dla serwera aplikacji, a następnie uruchamiam komendę:

C:\WrkOpenEdge117\oepas1\bin\deploySOAP.bat custorder.wsm ROOT


Web serwis jest już wdrożony. Restartuję serwer aplikacji i przystępuję do testów. Najpierw FindCustomerByName.

DEFINE VARIABLE NAME AS CHARACTER NO-UNDO.
DEFINE VARIABLE CustomerNumber AS INTEGER NO-UNDO.
DEFINE VARIABLE result AS CHARACTER NO-UNDO.
    
DEFINE VARIABLE hWebService AS HANDLE NO-UNDO.
//DEFINE VARIABLE CustomerOrderObj AS HANDLE NO-UNDO.
DEFINE VARIABLE hPortType AS HANDLE NO-UNDO.

DEFINE VARIABLE lReturn AS LOGICAL NO-UNDO.

CREATE SERVER hWebService.

lReturn = hWebService:CONNECT("-WSDL 'http://192.168.1.21:8810/soap/wsdl?targetURI=CustOrder' 
                               -Port CustOrderObj").

RUN CustOrderObj SET hPortType ON SERVER hWebService.

RUN FindCustomerByName IN hPortType(INPUT "Lift Tours", INPUT-OUTPUT CustomerNumber, OUTPUT result).

MESSAGE CustomerNumber
    VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.

W wyniku dostajemy nr klienta dla zadanej nazwy.

Test dla GetOrderDetails wygląda podobnie.

DEFINE TEMP-TABLE OrderDetails
    FIELD OrderNum LIKE Order.OrderNum
    FIELD SalesRep LIKE Order.SalesRep
    FIELD OrderDate LIKE Order.OrderDate
    FIELD ShipDate LIKE Order.ShipDate
    FIELD TotalDollars AS DECIMAL
    FIELD OrderStatus LIKE Order.OrderStatus.
    
DEFINE VARIABLE hWebService AS HANDLE NO-UNDO.
DEFINE VARIABLE CustomerOrderObj AS HANDLE NO-UNDO.
DEFINE VARIABLE hPortType AS HANDLE NO-UNDO.
DEFINE VARIABLE result AS CHARACTER NO-UNDO.
DEFINE VARIABLE lReturn AS LOGICAL     NO-UNDO.

CREATE SERVER hWebService.

lReturn = hWebService:CONNECT("-WSDL 'http://192.168.1.21:8810/soap/wsdl?targetURI=CustOrder' 
                               -Port CustOrderObj").

RUN CustOrderObj SET hPortType ON SERVER hWebService.

RUN GetOrderDetails IN hPortType(INPUT 3, OUTPUT RESULT, OUTPUT TABLE OrderDetails).


FOR EACH OrderDetails:
   DISPLAY OrderDetails.
END.

I wynik…

Pragnę zwrócić tutaj uwagę na dwie sprawy. Po pierwsze dla serwisów SOAP nie trzeba było definiować specjalnego projektu w OE Dev Studio. Po drugie nie trzeba było instalować i konfigurować Java Servlet Engine, Web Servera i WS adaptera, bo PASOE wszystko to już zawiera. To ogromne zaleta i ułatwienie pracy.
Życzę udanych projektów!

Web Serwisy SOAP i serwer aplikacji PAS cz.I

Tak się złożyło, że ostatnio kilkakrotnie byłem pytany przez klientów o  wystawianie web serwisów typu SOAP. Takie serwisy określane są czasem jako te “stare” w stosunku do serwisów typu REST. Są większym obciążeniem dla sieci (format danych XML) ale od wielu lat umożliwiają integrację/wymianę danych między różnymi systemami. Wspierane są też przez tradycyjny OpenEdge AppServer.

Pytanie, na które teraz należy odpowiedzieć to, czy nowy serwer aplikacji PASOE, lub jak kto woli PAS (Progress Application Server for OpenEdge) obsługuje także serwisy SOAP.

Żeby odpowiedzieć na to pytanie przyjrzyjmy się najpierw właściwościom pliku dla lokalnej instancji serwera w katalogu roboczym …\oepas1\conf\openedge.properties. W pliku tym znajdziemy wpisy:

[oepas1.ROOT.SOAP],

[oepas1.ROOT.REST],

[oepas1.ROOT.WEB],

[oepas1.ROOT.APSV].

Są to cztery warstwy transportowe; dla każdej zainstalowany i skonfigurowany jest adapter. Żeby wiedzieć czy dany adapter jest włączony trzeba sprawdzić ustawienie adapterEnabled; jeśli 1 to włączony. Pamiętajmy, że te ustawienia lepiej zmieniać poleceniem z linii komend niż bezpośrednio w pliku, a więc np.:
oeprop oepas1.ROOT.SOAP.adapterEnabled=1

Innym miejscem gdzie możemy sprawdzić powyższe ustawienia jest OpenEdge Explorer. Należy otworzyć widok w instancji serwera dla aplikacji webowej ABL ROOT (ABL WebApp: ROOT).

OK, ale jak w ogóle przygotować kod aplikacji do wystawienia za pomocą serwisu SOAP? Pierwszy etap jest dokładnie taki sam jak dla klasycznego AppServera. Ponieważ nie wszyscy korzystali dotychczas z tej metody omówimy ją na prostym przykładzie.

Mamy 2 oddzielne procedury, które chcemy wyświetlić w postaci web serwisów. Pierwsza FindCustomerByName.p zwraca numer rekordu Customer dla zadanej wartości Name.

DEFINE INPUT PARAMETER NAME AS CHARACTER.
DEFINE INPUT-OUTPUT PARAMETER CustomerNumber AS INTEGER.

FIND FIRST Customer WHERE Customer.NAME = NAME NO-ERROR.
IF AVAILABLE Customer THEN
    CustomerNumber = Customer.CustNum.
ELSE
    CustomerNumber = ?.

Druga procedura GetOrderDetails.p zwraca tablicę tymczasową zawierającą dane zamówień dla wybranego rekordu Customer.

DEFINE INPUT PARAMETER thisCust AS INTEGER NO-UNDO.

DEFINE TEMP-TABLE OrderDetails
    FIELD OrderNum LIKE Order.OrderNum
    FIELD SalesRep LIKE Order.SalesRep
    FIELD OrderDate LIKE Order.OrderDate
    FIELD ShipDate LIKE Order.ShipDate
    FIELD TotalDollars AS DECIMAL
    FIELD OrderStatus LIKE Order.OrderStatus.
    
DEFINE OUTPUT PARAMETER TABLE FOR OrderDetails.

FUNCTION GetTotalDollars RETURNS DECIMAL PRIVATE
  (OrderNum AS INTEGER)  FORWARD.

FIND FIRST Customer WHERE Customer.CustNum = thisCust NO-ERROR.
IF NOT AVAILABLE Customer THEN RETURN ERROR. 

EMPTY TEMP-TABLE OrderDetails.

FOR EACH Order WHERE Order.CustNum = thisCust:
    CREATE OrderDetails.
        ASSIGN 
            OrderDetails.OrderNum = Order.OrderNum
            OrderDetails.SalesRep = Order.SalesRep
            OrderDetails.OrderDate = Order.OrderDate
            OrderDetails.ShipDate = Order.ShipDate
            OrderDetails.OrderStatus = Order.OrderStatus.

        OrderDetails.TotalDollars = GetTotalDollars(Order.OrderNum).
END.

FUNCTION GetTotalDollars RETURNS DECIMAL PRIVATE
  (OrderNum AS INTEGER) :

    DEFINE VARIABLE TotalDollars AS DECIMAL NO-UNDO INITIAL 0.0.

    FOR EACH OrderLine WHERE OrderLine.OrderNum = OrderNum:
        TotalDollars = TotalDollars + OrderLine.ExtendedPrice.
    END.
    RETURN TotalDollars.
END FUNCTION.

Obie procedury kompilujemy do postaci r-kodu.
Teraz otwieramy narzędzie Proxy Generator. Służy ono do generowania plików tzw. proxy do integracji z interfejsem Javy i .NET, a także do definiowania web serwisów SOAP.
Definiowanie serwisów jest bardzo proste. Tworzymy nowy projekt np. CustOrder i podajemy ścieżkę do r-kodów.

Następnie w menu Procedure -> Add dodajemy 2 r-kody jako procedury Non-persistent.

Teraz przechodzimy do ustawień: Options -> Preferences. Ustawiamy Client type: Web Service oraz Output directory gdzie zostaną zapisane wygenerowane pliki. Pozostałe ustawienia są wykorzystywane dla klasycznego AppServera i zostaną zignorowane przez serwer PASOE. Przypominam, że dla nowego serwera aplikacji nie trzeba instalować i konfigurować JSE, adaptera WSA ponieważ te elementy są już wbudowane w produkt.

Teraz wybieramy opcję Generate i OK. W wybranym katalogu pojawiły się pliki CustOrder.wsm oraz CustOrder.wsdl (oraz log).
O tym jak wykorzystać te pliki i jak wdrożyć web serwis w serwerze aplikacji PASOE napiszę następnym razem.