PASOE – OEMANAGER

Właśnie pojawiła się nowa wersja OpenEdge LTS 12.8 a wraz z nią sporo nowości. Poświęcę im cały artykuł, ale to następnym razem. Teraz chciałbym skupić się na jednym elemencie dotyczącym serwera aplikacji PASOE, a mianowicie na narzędziu OEMANAGER.
Pamiętacie może, że dla klasycznego appservera czy webspeeda można było uruchomić z poziomu wiersza poleceń (proenv) komendę ASBMAN czy WTBMAN aby uzyskać status procesu i podstawowe metryki.
Podobnie jest z OEMANAGER – można uzyskać wszystkie potrzebne metryki w jednym wywołaniu, w przeciwieństwie do innych sposobów jak wywołania API. Ponadto, narzędzie OEMANAGER może być wykorzystywane podczas runtime’u.
Aby można było z niego skorzystać instancja musi zawierać wdrożone aplikacje: OpenEdge Manager (oemanager.war) and Tomcat Manager (manager.war). Możemy wdrożyć je odpowienimi poleceniami do istniejącej instancji lub stworzyć nową z już wdrożonymi tymi aplikacjami używając opcji -f. Poniżej zamieszczam komendę utworzenia takiej instancji mypasoe. Do niedawna zalecani nie instalować tych aplikacji w środowisku produkcyjnym ale obecnie nie ma takich przeciwskazań o ile odpowienio je zabezpieczymy. Jednym takim elementem zabezpieczeń jest oczywista zmiana domyślnych haseł. Po resztę informacji odsyłam do dokumentacji. Ja w tym przykładzie pozostanę przy domyślnych hasłach, a dlaczego, to zaraz pokażę.

pasman create -v -f -p 8813 -P 8814 -s 8815 %WRKDIR%\mypasoe
Teraz uruchamiam instancję poleceniem:
pasman oeserver -start -I mypasoe
Jest to nowsza forma komendy tcman (pasman) pasoestart, wprowadzona od OE 12.5. Po prawidłowym uruchomieniu procesu Tomcata nie oznacza, że wszystkie komponenty OpenEdge zostały uruchomione. Polecenie tcman -oeserver start sprawdza logi pod kątem takich błędów. Polecenie posiada kilka jeszcze innych opcji, a więc ci, którzy mają wersję 12.5+ powinni korzystać właśnie z niej.

Po uruchomieniu oemanager dostajemy poniższą długą listę opcji.

     [echo]  TCMAN Shortcuts:
     [echo]
     [echo]  oemanager query    - Use TCMAN to query the PAS instance
     [echo]
     [echo]  oemanager startup  - Use TCMAN to start the PAS instance
     [echo]                       [OPTIONAL] timeout=300 - Time (seconds) to wait for a proper startup
     [echo]
     [echo]  oemanager shutdown - Use TCMAN to stop the PAS instance
     [echo]                       [OPTIONAL] timeout=300 - Time (seconds) to wait for a proper shutdown
     [echo]
     [echo]
     [echo]  Support Tools:
     [echo]
     [echo]  oemanager inventory - Bundle useful PAS instance files (as .zip) for support tickets
     [echo]
     [echo]
     [echo]  Status/Info:
     [echo]
     [echo]  oemanager status - [RO] Obtain MSAgent/connection status information for an ABL App
     [echo]                     [OPTIONAL] basemem=819200 - Minimum memory threshold (bytes) to consider as 'unused' agent sessions
     [echo]
     [echo]  oemanager stacks - [RO] Obtain stack information for all MSAgents for an ABL App
     [echo]
     [echo]  oemanager flush  - [RO] Flush the available deferred log buffer to agent log file
     [echo]
     [echo]  oemanager locks  - [RO] Display database users and their table locks related to an MSAgent-Session
     [echo]                     This utilizes a single DBConnection; edit the 'locks' task in build.xml to add more as necessary
     [echo]                     Note: Only provides session data if using self-service DB connections for OE versions under 12.5
     [echo]                     [REQUIRED] pf=[PF_NAME] - PF file to use for database connection(s)
     [echo]
     [echo]  oemanager users  - [RO] Alias for 'locks' task
     [echo]
     [echo]
     [echo]  Agent Management:
     [echo]
     [echo]  oemanager add     - Add (read: start) one new MSAgent for an ABL App
     [echo]
     [echo]  oemanager close   - Perform a 'soft restart' of an ABL App (runs: status, flush + trimhttp + stop, status)
     [echo]                                       For this task the 'trimhttp' will be called with the termination option 1 (forced)
     [echo]                      [REQUIRED] webapp=[WEBAPP_NAME] - WebApp for Tomcat Manager to terminate active client sessions
     [echo]                                  The given WebApp is expected to be associated with the provided ablapp name
     [echo]                      [OPTIONAL] sleep=1 - Sleep time in minutes after stop, prior to final 'status' output
     [echo]
     [echo]  oemanager clean   - Alias for 'close' task [Deprecated]
     [echo]
     [echo]  oemanager refresh - Refresh ABL Sessions for each MSAgent for an ABL App (OE 12 Only)
     [echo]                      Note: This will essentially terminate all sessions (gracefully),
     [echo]                            and prepare the Agent to pick up any R-code changes
     [echo]
     [echo]  oemanager reset   - Reset an aspect of each MSAgent for an ABL App
     [echo]                      [REQUIRED] resettype=stats [stats|logs]
     [echo]
     [echo]  oemanager stop    - Gracefully stop one or all MSAgents (+stacks output) for an ABL App
     [echo]                      [OPTIONAL] waitfinish=120000 - How long to wait (milliseconds) if the MSAgent is busy serving a request
     [echo]                      [OPTIONAL]  waitafter=60000  - Additional time to wait (milliseconds) before killing [hard stop] the MSAgent
     [echo]                      [OPTIONAL]        pid=[AGENT_PID] - Numeric process ID for a specific MSAgent to be stopped
     [echo]
     [echo]
     [echo]  Session Management:
     [echo]
     [echo]  Note: All trim actions listed below will write application stack information to a file.
     [echo]
     [echo]  oemanager trimsingle - Trim a single ABL Session (via the Agent Manager) for a specific MSAgent
     [echo]                         [REQUIRED]          pid=[AGENT_PID]  - Numeric process ID of the MSAgent for context
     [echo]                         [REQUIRED]       sessid=[SESSION_ID] - Numeric ID for the ABL Session to be stopped
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]  oemanager trimall    - Trim all available ABL Sessions (via the Agent Manager) for each MSAgent for an ABL App
     [echo]                         Note: For any busy sessions considered stuck use 'trimhttp' with a specific Session ID
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish/stop
     [echo]
     [echo]  oemanager trimidle   - Trim only the IDLE ABL Sessions (via the Agent Manager) for each MSAgent for an ABL App
     [echo]                         Allows for manually scaling down an MSAgent which may have many unused ABL Sessions
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]  oemanager trimhttp   - Trim one or all Client HTTP Sessions (via the Session Manager) for an ABLApp + WebApp
     [echo]                         Terminating a client HTTP session will also terminate its associated ABL Session
     [echo]                         [REQUIRED]       webapp=[WEBAPP_NAME] - WebApp for Tomcat Manager to terminate active sessions
     [echo]                                           The given WebApp is expected to be associated with the provided ablapp name
     [echo]                         [OPTIONAL]       sessid=[SESSION_ID]  - Alphanumeric Client Session ID to be stopped
     [echo]                                           When no session ID provided, all available Client HTTP Sessions will be expired
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]
     [echo] Current parameter values, override via command line or 'oemanager.properties':
     [echo]     scheme=http
     [echo]       host=localhost
     [echo]       port=8813
     [echo]     userid=tomcat
     [echo]     passwd=tomcat
     [echo]     ablapp=mypasoe

Niektóre są oczywiste jak: start, stop, status itp. ale niektóre wyglądają bardzo ciekawie, np. opcja flush – pomaga znaleźć przyczynę powtarzających się awarii agenta, poprzez opóżnione zrzucenie informacji ze specjalnego bufora do pliku.
Na samym dole widać użytkownika i hasło do procesu Tomcata. Należy dla bezpieczeństwa wejść do pliku oemanager.properties, znajdującego się w podkatalogu bin dla instancji, odkomentować dwie linie i ustawić puste dane logowania, jak na poniższym obrazku.

OK, poniżej zamieściłem wynik działania komendy oemanager status.

 [PCTRun]  PASOE Instance: http://localhost:8813
   [PCTRun]
   [PCTRun] ABL Application Information [mypasoe - v12.8.0 ( 2023-12-21 )]
   [PCTRun]     WebApp: ROOT
   [PCTRun]       APSV: ENABLED
   [PCTRun]       SOAP: ENABLED
   [PCTRun]       REST: ENABLED
   [PCTRun]        WEB: ENABLED
   [PCTRun]
   [PCTRun] Manager Properties
   [PCTRun]             Maximum Agents:           2
   [PCTRun]             Minimum Agents:           1
   [PCTRun]             Initial Agents:           1
   [PCTRun]     Max. Connections/Agent:         200
   [PCTRun]     Max. ABLSessions/Agent:         200
   [PCTRun]         Idle Conn. Timeout:     300.000 ms (00:00:05:00.000)
   [PCTRun]       Idle Session Timeout:   1.800.000 ms (00:00:30:00.000)
   [PCTRun]         Idle Agent Timeout:   1.800.000 ms (00:00:30:00.000)
   [PCTRun]      Idle Resource Timeout:           0 ms (00:00:00:00.000)
   [PCTRun]         Conn. Wait Timeout:       3.000 ms (00:00:00:03.000)
   [PCTRun]       Request Wait Timeout:      15.000 ms (00:00:00:15.000)
   [PCTRun]     Initial Sessions/Agent:           2
   [PCTRun]     Min. Avail. Sess/Agent:           1
   [PCTRun]
   [PCTRun] > Agent PID 13208: AVAILABLE
   [PCTRun]     Est. Agent Lifetime: 00:00:00:00.000
   [PCTRun]     DynMax ABL Sessions:          200
   [PCTRun]      Total ABL Sessions:            2
   [PCTRun]      Avail ABL Sessions:            2
   [PCTRun]        Open Connections:            1
   [PCTRun]         Overhead Memory: 13.897 KB
   [PCTRun]
   [PCTRun]     SESSION ID      STATE           STARTED                         LIFETIME          SESS. MEMORY     ACTIVE MEM.   REQUESTS       BOUND/ACTIVE CLIENT SESSION
   [PCTRun]                4    IDLE            18/02/2024 15:18:40,229-01:00   00:00:01:28.600         742 KB         742 KB           0        -
   [PCTRun]                7    IDLE            18/02/2024 15:18:40,229-01:00   00:00:01:28.600         742 KB         742 KB           0        -
   [PCTRun]       Active Agent-Sessions: 0 of 2 (0% Busy)
   [PCTRun]     Utilized Agent-Sessions: 0 of 2 (>801 KB)
   [PCTRun]        Approx. Agent Memory: 15.380 KB
   [PCTRun]
   [PCTRun] Session Manager Metrics (Count-Based)
   [PCTRun]            # Requests to Session:           0
   [PCTRun]           # Agent Responses Read:           0 (0 Errors)
   [PCTRun]         # Agent Requests Written:           0 (0 Errors)
   [PCTRun]     Concurrent Connected Clients:           0 (Max: 0)
   [PCTRun]       # Reserve ABLSession Waits:           0
   [PCTRun]     # Reserve ABLSession Timeout:           0
   [PCTRun]
   [PCTRun] Client HTTP Sessions: 0

Administrator otrzymuje natychmiastową całościową informację o warstwach transportowych, właściwościach agentów itd.
Zachęcam do przetestowania i korzystania z opcji komendy OEMANAGER.

Strukturalna obsługa błędów w ABL cz. II

W poprzednim odcinku obiecałem napisać o tym jak utworzyć własną klasę obsługi błędów oraz pokazać, że strukturalną obsługę błędów można stosować także w przypadku procedur uruchamianych na serwerze aplikacji. Obie te kwestie pokażę na jednym przykładzie.

W Developer’s Studio klasę definiujemy najwygodniej przy pomocy wizarda, tak jak każda inną klasę. Musimy pamiętać, że dziedziczy ona z klasy Progress.Lang.AppError oraz że trzeba zdefiniować ją jako Serializable, co oznacza, że instancja klasy może być przekazywane przez wartość pomiędzy sesjami AVM (ABL Virtual Machine).

Poniżej widzimy gotową klasę Errors.myError zawierającą właściwości: Errormessage, ErrorNum oraz ProcName oraz dwie metody GetErrorMessage() i GetVerboseErrorMessage(). Posłużą one do własnej obsługi błędów.
Pierwsza metoda zwraca jedynie komunikat wraz z numerem błędu. GetVerboseErrorMessage() zawiera także nazwę procedury, w której ten błąd wystąpił. Gdybyśmy tworzyli program złożony z obiektów moglibyśmy użyć nazwy klasy i metody aby mieć więcej informacji o lokalizacji wystąpienia błędu itp.

USING Progress.Lang.*.
USING Progress.Lang.AppError.

BLOCK-LEVEL ON ERROR UNDO, THROW.

CLASS Errors.myError INHERITS AppError SERIALIZABLE: 

    DEFINE PUBLIC PROPERTY ErrorMessage AS CHARACTER NO-UNDO 
    GET.
    SET. 

    DEFINE PUBLIC PROPERTY ErrorNum AS INTEGER NO-UNDO 
    GET.
    SET. 
    
    DEFINE PUBLIC PROPERTY ProcName AS CHARACTER NO-UNDO 
    GET.
    SET. 

    CONSTRUCTOR PUBLIC myError (
        INPUT pErrorNum AS INTEGER,
        INPUT pErrorMessage AS CHARACTER,
        INPUT pProcName AS CHARACTER):            
        SUPER ().
        ErrorNum = pErrorNum.            
        ErrorMessage = pErrorMessage.
        ProcName = pProcName.
    END CONSTRUCTOR.

    METHOD PUBLIC CHARACTER GetErrorMessage(  ):
        DEFINE VARIABLE res AS CHARACTER NO-UNDO.
        res =  ErrorMessage + ". " + STRING(ErrorNum).
        RETURN res.
    END METHOD.

    METHOD PUBLIC CHARACTER GetVerboseErrorMessage(  ):
        DEFINE VARIABLE res AS CHARACTER NO-UNDO.
        res =  ErrorMessage + ". " + STRING(ErrorNum) + ". Proc: " + ProcName.
        RETURN res.
    END METHOD.    
    
END CLASS.  

Poniższy przykład jest analogiczny do tego z poprzedniego odcinka – został zmodyfikowany do uruchomienia na serwerze aplikacji, oczywiście z własną obsługą błędów.

USING Errors.myError.

VAR INT i = 1.
DEFINE VARIABLE hServer AS HANDLE NO-UNDO.
DEFINE VARIABLE lReturn AS LOGICAL NO-UNDO.

CREATE SERVER hServer.
lReturn = hServer:CONNECT("-URL http://localhost:8810/apsv
  -sessionModel Session-managed"). 

IF NOT lReturn THEN DO:
  DELETE OBJECT hServer NO-ERROR.
  RETURN ERROR "Failed to connect to the ABL web application: " + RETURN-VALUE.
END.

RUN catcherror-pasoe-myerror.p ON hServer (INPUT 3000).

CATCH myerr AS MyError:
    MESSAGE myerr:GetVerboseErrorMessage()
        VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
/************************************************************/

/* catcherror-pasoe-myerror.p */
BLOCK-LEVEL ON ERROR UNDO, THROW.

USING Errors.myError.

DEFINE INPUT PARAMETER icustNum AS integer.
DEFINE VARIABLE ProcName AS CHARACTER.


FIND customer WHERE customer.custnum = icustNum.
if country NE "Poland" THEN DO:
    ProcName = ENTRY(1, PROGRAM-NAME(1),' ').
    UNDO, THROW NEW myError(555, "Bad country", ProcName).

END. 

Ponieważ użyłem metody GetVerboseErrorMessage() otrzymuję cały komunikat z nazwą procedury w której wystąpił błąd.