C/C++ / CGI / Sieć Novell / PHP / Java / SQL / Oracle / WebSphere MQ / WebSphere Message Broker / JavaScript / Humor / IT Quiz


WebSphere Message Broker

Informacje podstawowe

O WebSphere Message Broker
*Agregacja
*Nody HTTP
*Nody HTTP i SSL
Web Services i Basic-Auth
Publish/Subscribe
Error Handling
*Manipulowanie treścią komunikatu
*Referencje
*Trace
Debuging
Współdzielenie danych w ramach przepływu
Operacje na bazie danych
Parametry UDP
Nod MQGet
*Nody typu Timer
Własne nody JAVA
*Wywołanie kodu JAVA z ESQL
Modyfikacja parametrów pliku BAR
*CVS runtime versioning i keywords
Dostępne parametry brokera
* Accounting and Statistics
Security
Configuration Manager Proxy
Wydajność

Literatura

Jeden plik

Legenda
w przygotowaniu
* w trakcie tworzenia


O WebSphere Message Broker

Podczas tego kursu postaram się omówić narzędzie WebSphere Message Broker firmy IBM. Kurs ten jest w trakcie tworzenia i może się zdarzyć, że informacje w nim zawarte mogą wydawać się chaotycznie posegregowane. Nie będzie on również omawiał narzędzia od podstaw, lecz najciekawsze według mnie jego elementy, dlatego może się okazać, że dla osób nie znających chociaż podstaw WebSphere Message Broker będzie on niezrozumiały. Będę starał się cały czas rozwijać go, ponieważ jest to moja specjalizacja (systemy kolejkowe) i docelowo kurs ten będzie zawierał podstawowe informacje jak również zaawansowane dla osób znających temat. Na dzień dzisiejszy jest w nim trochę informacji podstawowych oraz trochę informacji wchodzących głębiej w charakterystykę narzędzia.


Agregacja

Agregacja jest ciekawą technika pozwalającą na wysłaniu z przepływu kilku zapytań, agregacji odpowiedzi, a następnie na podstawie odebranych odpowiedzi przygotowanie zagregowanej odpowiedzi.

Do obsługi agregacji wykorzystujemy nody AggregateControl, AgregateRequest oraz AggregateReply.

AggregateControl

Nod AggregateControl służy do zaznaczenia rozpoczęcia procesu agregacji. Najważniejsze parametry noda ta:

Aggregate name : nazwa agregacji (unikalny identyfikator)
Timeout : czas jaki będziemy oczekiwać na odpowiedź

AgregateRequest

Nod AgregateRequest służy do określenia komunikatu MQ, który jest częścią procesu agregacji. Umieszcza się go za node MQOutput aby zapisać informacje o tym na jakie pytania w procesie agregacji oczekujemy odpowiedzi. Najważniejszy parametr noda to:

Folder name : Folder, w którym będzie zapisana odpowiedz (unikalny identyfikator zapytania w ramach agregacji)

AggregateReply

Nod AggregateReply służy do magazynowania odpowiedzi w ramach agregacji i w efekcie zwraca drzewo, ze wszystkimi odpowiedziami. Najważniejsze parametry noda to:

Aggregate name
nazwa agregacji (unikalny identyfikator) skorelowany z tym samym parametrem noda AggregateControl

Unknow message timeout
określa w sekundach czas jaki proces oczekuje w przypadku odczytania komunikatu, które nie może skorelować z żadnym z wysłanych wcześniej zapytań zanim wyśle go do terminala "Unknown"

Najważniejsze terminale noda to:

Failure : standardowy terminal do którego przekazywane są komunikaty, które spowodowały bład w procesowaniu
Unknown : do tego terminala przekazywane są komunikaty, które nie można skorelować z żadnym wysłanym wcześniej zapytaniem
Out : do tego terminala przekazywany jest komunikat grugujące wszystkie zebrane odpowiedzi
Timeout : do tego terminala przekazywany jest komunikat grupujący wszystkie odpowiedzi ( które nadeszły w zakładnym czasie określonym na nodzie AggregateControl)

Scenariusz

Poniżej postaram się przedstawić scenariusz opisujący wykorzystanie agregacji w praktyce. Zaimplementujemy przepływ będący WebServices, którego zadaniem będzie zwrócenie liczby. WebService przygotuje i wyśle dwa zapytania MQ. Na każde zapytanie MQ otrzymamy w odpowiedzi liczbę. Zadaniem WebSevice jest zsumowanie tych liczb i zwrócenie tej sumy jako odpowiedzi WS.

Pierwszym elementem przepływu jest nod HTTPInput oczekujący na połączenia WS. Dalej mamy nod AggregateControl, którego ustawiamy parametr Aggregate name na Aggr oraz  Timeout na 15 sekund. W ten sposób określiliśmy identyfikator dla agregacji oraz określiliśmy skończony czas jaki oczekujemy na odpowiedź.

Dalej mamy nod Compute, którego zadaniem jest przygotowanie komunikatów MQ

W pierwszej części w module deklarujemy namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie deklarujemy funkcje PrepareMQMDRequest, która przygotuje request MQ i ustawia odpowiednie parametry nagłówka MQMD.

        CREATE FUNCTION PrepareMQMDRequest()
        BEGIN

                SET OutputLocalEnvironment=InputLocalEnvironment;
                SET OutputRoot.Properties.MessageSet = '';
                SET OutputRoot.Properties.MessageType = '';
                SET OutputRoot.Properties.MessageFormat = '';

                SET OutputRoot.MQMD.StrucId = MQMD_STRUC_ID;
                SET OutputRoot.MQMD.Version = MQMD_CURRENT_VERSION;
                SET OutputRoot.MQMD.Format = MQFMT_STRING;
                SET OutputRoot.MQMD.Expiry= 300; --30 sekund
                SET OutputRoot.MQMD.MsgType = 1;
                SET OutputRoot.MQMD.Priority= 3;
                SET OutputRoot.MQMD.ReplyToQ = 'MQSI.MK_Q.IN';
        END;


Dalej w głónej funkcji Main w pierwszej części tworzymy odpowiednie domeny MQMD oraz XMLNS, usuwamy niepotrzebne elementy HTTPInputHeader oraz MRM, wywołujemy funkcje PrepareMQMDRequest wypełniającą odpowiednie pola MQMD, przygotowujemy dane komunikatu MQ na podstawie danych wejściowych. oraz propagujemy komunikat do terminala out1.

                CREATE NEXTSIBLING OF OutputRoot.Properties DOMAIN 'MQMD';
                CREATE NEXTSIBLING OF OutputRoot.MQMD DOMAIN 'XMLNS';

                SET OutputRoot.HTTPInputHeader=null;
                SET OutputRoot.MRM=null;                

                CALL PrepareMQMDRequest() ;
                SET OutputRoot.XMLNS.DATA.v1=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v1;
                SET OutputRoot.XMLNS.DATA.v2=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v2;

                PROPAGATE TO TERMINAL 'out1';



W ten sposób przygotowaliśmy pierwszy komunikat, który będzie wysłany do pierwszej aplikacji. Dalej przygotowujemy drugi komunikat, wywołujemy funkcje PrepareMQMDRequest wypełniającą odpowiednie pola MQMD, przygotowujemy dane komunikatu MQ na podstawie danych wejściowych. oraz propagujemy komunikat do terminala out2.

                CALL PrepareMQMDRequest() ;

                SET OutputRoot.XMLNS.DATA.MULTI.v1=
                InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v1;

                SET OutputRoot.XMLNS.DATA.MULTI.v2=
                InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v2;

                PROPAGATE TO TERMINAL 'out2';

W ten sposób przygotowaliśmy pierwszy komunikat, który będzie wysłany do drugiej aplikacji.

Dalej musimy jeszcze utworzyć komunikat, który będzie zawierał informacje o identyfikatorze zapytania WS.Ten identyfikator będzie nam potrzebny później w celu skorelowania przygotowywanej odpowiedzi z zapytaniem WS.

Aby to zrobić wywołujemy funkcje PrepareMQMDRequest wypełniającą odpowiednie pola MQMD, przygotowujemy treść komunikatu uzupełniając go o przeczytany identyfikator zapytania WS. Następnie ustawiamy typ komunikatu na 2 czyli REPLY, propagujemy komunikat do terminala out oraz zwracamy w funkcji wartość FALSE.

                CALL PrepareMQMDRequest();

                SET OutputRoot.XMLNS.DATA.HTTPID=
                CAST (InputLocalEnvironment.Destination.HTTP.RequestIdentifier AS CHARACTER);

                SET OutputRoot.MQMD.MsgType = 2;
                PROPAGATE TO TERMINAL 'out';

                RETURN FALSE;

Dalej z noda Compute wyprowadzone są terminale, out1 do noda MQOutput wskazującego na kolejkę aplikacji 1, out2 do noda MQOutput wskazującego na kolejkę aplikacji 2 oraz out do noda MQOutput wskazującego na kolejkę, w której zapisywane są identyfikatory zapytania HTTP.

Dalej za każdym wymienionym wcześniej nodem MQOutput znajduje się nod AggregateRequest. Ich parametr Folder name ustawiony jest odpowiednio REQ1 dla kolejki 1, REQ2 dla kolejki 2 oraz HTTP dla kolejki HTTP. W ten sposób wysłaliśmy odpowiednie komunikaty oraz określiliśmy, że na agregacje składają sie 3 powyższe zapytania MQ.

W drugiej części przepływu mamy dwa nody MQInput, jeden oczekujący na kolejce, w której mają pojawić się odpowiedzi od obu aplikacji oraz drugi na kolejce zawierającej identyfikatory zapytań WS.

Nod MQInput związany z HTTP połączony jest z nodem Compute. Zadaniem tego noda jest przepisania pola MsgId do CorrelId. Pozwoli to poprawnie zidentyfikować komunikat jako odpowiedz na wysłany wcześniej komunikat.

                SET OutputRoot.MQMD.CorrelId=InputRoot.MQMD.MsgId;

Powyższy nod Compute oraz drugi nod MQInput związany z odpowiedziami z aplikacji połączony jest z nodem AggregateReply. Zadaniem tego noda jest magazynowanie napływających odpowiedzi danej agregacji. Parametr "Aggregate name" tego noda ustawiamy na Aggr, parametr "Unknow message timeout" na 0.
Jeśli nod ten w zadanym czasie określonym wcześniej w nodzie AggregateControl zbierze wszystkie odpowiedzi procesowanie przejdzie do terminala "out". Terminal ten połączony jest z nodem Compute.
W pierwszej części w module deklarujemy namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie w głównej metodzie Main deklarujemy dwie zmienne oraz podstawiamy pod nie dwie odpowiedzi od aplikacji.Następnie kopiujemy cały blok Properties oraz ustawiamy odpowiednie pola bloku Properties na wartości związane z MRM WebService.

        DECLARE response1 INTEGER;
        DECLARE response2 INTEGER;

        SET response1=InputRoot.ComIbmAggregateReplyBody.REQ1.XMLNS.RESPONSE.DATA.V;
        SET response2=InputRoot.ComIbmAggregateReplyBody.REQ2.XMLNS.RESPONSE.DATA.V;

        SET OutputRoot.Properties = InputRoot.Properties;

        SET OutputRoot.Properties.MessageSet = 'PHTM4NG002001';
        SET OutputRoot.Properties.MessageType = 'Envelope';
        SET OutputRoot.Properties.MessageFormat = 'XML1';

Dalej generujemy odpowiedz WS jako sumę zebranych odpowiedzi oraz ustawiamy identyfikator zapytania na podstawie przeczytanej wartości z wcześniej okreslonego folderu HTTP.

        SET OutputRoot.MRM.soapenv:Body.tns:addIntResponse.tns:addIntResult.v=
        response1+response2;

        SET OutputLocalEnvironment.Destination.HTTP.RequestIdentifier =
        CAST(InputRoot.ComIbmAggregateReplyBody.HTTP.XMLNS.DATA.HTTPID AS BLOB);

Dalej nod Compute podłączony jest do ostatniego noda w przepływie HTTPReply, którego zadaniem jest wysłanie odpowiedzi WS.

Jeśli z kolej nod AggregateReply nie zdoła w zadanym czasie określonym wcześniej w nodzie AggregateControl zebrać wszystkich odpowiedzi procesowanie z noda przejdzie do terminala "Timeout".

Tutaj mogliśmy oczywiście wysłać odpowiedz WS mówiący o błędzie.W naszym scenariuszu zdecydowałem się wysłać odpowiedź na podstawie tylko zebranych odpowiedzi.

W związku z tym terminal "Timeout" noda AggregateReply podłączamy do noda Compute, którego zadaniem jest przygotowanie odpowiedniej odpowiedzi WS.

W pierwszej części w module deklarujemy namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie w głównej metodzie Main deklarujemy zmienne określające odpowiedzi MQ oraz wartość jaką zwrócimy do WS. Podstawiamy pod dwie zmienne odpowiedzi od aplikacji oraz wyliczamy wartość response na podstawie odpowiedzi, które uzyskaliśmy.

                DECLARE response1 INTEGER;
                DECLARE response2 INTEGER;
                DECLARE response INTEGER;

SET response1=InputRoot.ComIbmAggregateReplyBody.REQ1.XMLNS.RESPONSE.DATA.V;
SET response2=InputRoot.ComIbmAggregateReplyBody.REQ2.XMLNS.RESPONSE.DATA.V;

                SET response=0;

                IF response1 IS NOT NULL THEN SET response=response+response1;
                END IF;
                IF response2 IS NOT NULL THEN SET response=response+response2;
                END IF;

Następnie kopiujemy cały blok Properties oraz ustawiamy odpowiednie pola bloku Properties na wartości związane z MRM WebService. Dalej generujemy odpowiedz WS jako sumę zebranych odpowiedzi oraz ustawiamy identyfikator zapytania na podstawie przeczytanej wartości z wcześniej określonego folderu HTTP.

        SET OutputRoot.Properties = InputRoot.Properties;

        SET OutputRoot.Properties.MessageSet = 'PHTM4NG002001';
        SET OutputRoot.Properties.MessageType = 'Envelope';
        SET OutputRoot.Properties.MessageFormat = 'XML1';

        SET OutputRoot.MRM.soapenv:Body.tns:addIntResponse.tns:addIntResult.v=response;

        SET OutputLocalEnvironment.Destination.HTTP.RequestIdentifier =
        CAST(InputRoot.ComIbmAggregateReplyBody.HTTP.XMLNS.DATA.HTTPID AS BLOB);

        SET Environment.HTTPID=InputRoot.ComIbmAggregateReplyBody.HTTP.XMLNS.DATA.HTTPID;

Na zakończenie wyjście out z noda Copute łączymy do omawianego wcześniej noda HTTPReply, którego zadaniem jest wysłanie przygotowanej odpowiedzi WS.


Nody HTTP

Message Broker posiada nody odpowiadające za komunikacje HTTP z i do Brokera.Nody te to:

HTTPRequest

Nod ten pozwala z brokera odpytać się zewnętrznego zasobu za pomocą HTTP.

HTTPInput

Nod ten pozwala na implementacje HTTP przez broker. Oczekuje on na zapytania HTTP z zewnątrz.

HTTPReply

Nod ten jest związany z HTTPInput i pozwala na wysłanie odpowiedzi HTTP po wcześniejszym przeprocesowaniuzapytania, które dotarło do noda HTTPInput.

HTTPRequest

Najważniejsze parametry noda HTTPRequest to:

Web Service URL

Parametr ten określa URL zdalnego Web Service. Jeśli chcemy możemy ta wartość ustawić dynamicznie w kodzie : SET OutputLocalEnvironment.Destination.HTTP.RequestURL='http://...';

Message Domain

Określa domenę przy parsowaniu. Możliwe opcje to XML, XMLNS, MRM i inne.

Message Set

Jeśli domenę określimy jako MRM, w tym polu wybieramy Message Set.

HTTPInput

Najważniejsze parametry noda HTTPInput to:

Path suffic for URL

Parametr ten określa URL pod jakim oczekujemy połączeń HTTP

Message Domain

Określa domenę przy parsowaniu. Możliwe opcje to XML, XMLNS, MRM i inne.

Message Set

Jeśli domenę określimy jako MRM, w tym polu wybieramy Message Set.

HTTPReply

Nod HTTPReply służy tylko i wyłącznie do wysłania odpowiedzi HTTP. Nie ma zbyt wielu opcji. Najciekawsze to te związane z timeoutami.

Scenariusze

Poniżej postaram się zebrać kilka możliwych scenariuszy wykorzystania nodów w przepływie.

Scenariusze - przepływ jako WebService

W tym scenariuszu zaimplementujemy WebService na brokerze. Będzie to prosty WebService, który będzie sumował dwie liczby i zwracał ich sumę. W tym scenariuszu przepływ będzie zawierał nod HTTPInput, Compute oraz HTTPReply. Jako domenę w nodzie HTTPInput ustawiamy MRM.

CREATE COMPUTE MODULE WebServiceInput_Compute

DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

CREATE FUNCTION processAddInt()
        BEGIN
                DECLARE v1 INTEGER;
                DECLARE v2 INTEGER;
                DECLARE v3 INTEGER;
                SET v1=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v1;
                SET v2=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v2;
                SET v3=v1+v2;
SET OutputRoot.MRM.soapenv:Body.tns:addIntResponse.tns:addIntResult.v=v3;
        END;

CREATE FUNCTION Main() RETURNS BOOLEAN
        BEGIN
                CALL CopyMessageHeaders();
                CALL processAddInt();
                RETURN TRUE;
        END;

Scenariusze - wywołanie WebService z przepływu

Pierwszym elementem w przepływie będzie nod MQInput pobierający komunikaty MQ z kolejki. Następnie znajduje się nod Compute.

W pierwszej części w module deklarujemy namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie deklarujemy funkcje, która przygotuje request WebService zasilając go danymi z komunikatu MQ

                CREATE FUNCTION processAddIntRequest()
        BEGIN
                SET OutputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v1= XMLNS.REQUEST.DATA.V1;
                SET OutputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v2= XMLNS.REQUEST.DATA.V2;
        END;

Następnie deklarujemy główną funkcje Main, a w niej w pierwszej części kopiujemy cały komunika i ustawiamy odpowiednie parametry Properties wskazujące na używany Message Set dla wywołania WebService.

                CALL CopyEntireMessage();                                

                SET OutputRoot.Properties.MessageSet = 'PHTM4NG002001';
                SET OutputRoot.Properties.MessageType = 'Envelope';
                SET OutputRoot.Properties.MessageFormat = 'XML1';
                SET OutputRoot.HTTPRequestHeader.SOAPAction = InputRoot.XMLNS.REQUEST.DATA.SOAPACTION;                

Kolejnym krokiem jest wywołanie zadeklarowanej wcześniej funkcji processAddIntRequest, ustawienie URL WebService jaki będziemy odpytywali oraz kopiujemy cały blok MQMD do Environment.

                                CALL processAddIntRequest();

                SET OutputLocalEnvironment.Destination.HTTP.RequestURL=InputRoot.XML.REQUEST.DATA.URL;

                SET Environment.myMQMD=InputRoot.MQMD;

W ten sposób przygotowaliśmy zapytanie WebServise. Następnym elementem przepływy jest nod HTTPRequest, a zaraz za nim następny nod Compute, którego celem jest interpretacja odpowiedzi.

W pierwszej części w module deklarujemy ponownie namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie deklarujemy funkcje, która przygotuje komunikat MQ zawierający odpowiedź pobrany z WebService

        CREATE FUNCTION processAddIntResponse()
        BEGIN
                SET OutputRoot.XML.DATA.RESULT=
                InputRoot.MRM.soapenv:Body.tns:addIntResponse.tns:addIntResult.v;
        END;

Następnie deklarujemy główną funkcje Main, a w niej w pierwszej części kopiujemy cały komunika i czyścimy wcześniej ustawione odpowiednie parametry Properties wskazujące na używany Message Set dla wywołania WebService.

                CALL CopyEntireMessage();

                SET OutputRoot.Properties.MessageSet = '';
                SET OutputRoot.Properties.MessageType = '';
                SET OutputRoot.Properties.MessageFormat = '';


Dalej w funkcji Main usuwamy niepotrzebne już bloki danych HTTPRequestHeader, MRM, HTTPResponseHeader

                SET OutputRoot.HTTPRequestHeader=null;
                SET OutputRoot.MRM=null;
                SET OutputRoot.HTTPResponseHeader=null;                


W kolejnej części funkcji Main tworzymy domenę MQMD zaraz po loku Properties, kopiujemy do bloku MQMD wcześniej zapisany w Environment blok myMQMD, ustawiamy parametr MsgType na MQMT_REPLY, parametr CorrelId na podstawie wejściowego MsgId, ustawiamy cel komunikatu oraz wywołujemy wcześniej zadeklarowaną funkcje processAddIntResponse.

CREATE NEXTSIBLING OF OutputRoot.Properties DOMAIN 'MQMD';

                SET OutputRoot.MQMD=Environment.myMQMD;
                SET OutputRoot.MQMD.CorrelId=Environment.myMQMD.MsgId;
                SET OutputRoot.MQMD.MsgType=MQMT_REPLY;

                SET OutputLocalEnvironment.Destination.MQ.DestinationData[1].queueName=
                Environment.myMQMD.ReplyToQ;

                SET OutputLocalEnvironment.Destination.MQ.DestinationData[1].queueManagerName=
                Environment.myMQMD.ReplyToQMgr;

                CALL processAddIntResponse();


Dalej w przepływie mamy nod MQOutput ustawiony parametr Destination mode na Destination List, który wyśle odpowiedź MQ do odpowiednio zadeklarowanej wcześniej kolejki.

Scenariusze - wywołanie MQ z przepływu implementującego WebService

W tym scenariuszu na brokerze będzie zdeployowany WebService, który na podstawie danych wejściowych będzie zapytanie wysyłał do odpowiedniej kolejki MQ i z drugiej strony będzie oczekiwał na odpowiedź MQ. Po otrzymaniu odpowiedzi MQ przepływ bedzie wysyłał odpowiedz WebService.

Pierwszym elementem przepływu jest nod HTTPInput, którego zadaniem jest oczekiwanie na zapytania WebService. Następnym elementem jest nod Compute, którego zadaniem jest przygotowanie komunikatu MQ.

W pierwszej części w module deklarujemy ponownie namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Wewnątrz tego modułu tworzymy domenę MQMD, XMSNS, kopiujemy LocalEnvironment, który zawiera identyfikator zapytania WebService RequestIdentifier, które musimy przekazać dalej oraz wskazujemy kolejkę docelową aplikacji.

                CREATE NEXTSIBLING OF OutputRoot.Properties DOMAIN 'MQMD';
                CREATE NEXTSIBLING OF OutputRoot.MQMD DOMAIN 'XMLNS';
                SET OutputLocalEnvironment.Destination.MQ.DestinationData[1].queueName='MQSI.MK_Q.OUT';
                SET OutputLocalEnvironment=InputLocalEnvironment;                


Następnie usuwamy pola Properties związane z WebService, ustawiamy odpowiednie pola MQMD, tworzymy zapytanie MQ na podstawie danych wejściowych i usuwamy zbędne pola HTTPInputHeader oraz MRM.

                SET OutputRoot.Properties.MessageSet = '';
                SET OutputRoot.Properties.MessageType = '';
                SET OutputRoot.Properties.MessageFormat = '';

                SET OutputRoot.MQMD.StrucId = MQMD_STRUC_ID;
                SET OutputRoot.MQMD.Version = MQMD_CURRENT_VERSION;
                SET OutputRoot.MQMD.Format = MQFMT_STRING;
                SET OutputRoot.MQMD.Expiry= 300; --30 sekund
                SET OutputRoot.MQMD.ReplyToQ = 'MQSI.MK_Q.IN';

                SET OutputRoot.XMLNS.DATA.v1=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v1;
                SET OutputRoot.XMLNS.DATA.v2=InputRoot.MRM.soapenv:Body.tns:addInt.tns:intPair.v2;

                SET OutputRoot.HTTPInputHeader=null;
                SET OutputRoot.MRM=null;                


Następnie w przepływie znajduje się nod MQOutput, który wysyła zapytanie do aplikacji MQ, a zaraz za nim nod Compute, którego zadaniem jest wrzucenie dodatkowego komunikatu MQ zawierającego identyfikator zapytania WebServices do kolejki.
W module Compute w pierwszej częsci kopiujemy Properties, MQMD oraz wskazujemy kolejkę w której zapisywane są identyfikatory zapytania WebService.

                SET OutputRoot.Properties = InputRoot.Properties;
                SET OutputRoot.MQMD = InputRoot.MQMD;

                SET OutputLocalEnvironment.Destination.MQ.DestinationData[1].queueName='MQSI.MK_Q.HTTP';


Następnie ustawiamy pole CorrelId komunikaty HTTP przepisując pole MsgId z wcześniej wysłanego komunikatu do kolejki aplikacyjnej oraz tworzymy treść komunikatu zawierającego identyfikator zapytania WebService.

                SET OutputRoot.MQMD.CorrelId= InputLocalEnvironment.WrittenDestination.MQ.DestinationData.msgId;

                SET OutputRoot.XMLNS.HTTP.RequestIdentifier   = InputLocalEnvironment.Destination.HTTP.RequestIdentifier;


Następnie mamy nod MQOutput którego zadaniem jest wrzucenie komunikatu do dedykowanej kolejki HTTP.

Druga część przepływu odpowiada za odebranie komunikatu MQ odpowiedzi skorelowaniu go z komunikatem zawierającym identyfikator HTTP i wysłaniu odpowiedzi WebService do pytającego.

Pierwszym elementem tej części jest nod MQInput oczekujący na kolejce w której pojawiają się odpowiedzi z aplikacji. Zaraz za nim jest nod MQGet z odpowiednio ustawionym parametrem wait interval oraz zaznaczonym polu Get By correlation ID i oczekującym na kolejce HTTP. Oznacza to, że nod pobierze z kolejki tylko komunikat którego Correlation Id jest identyczne jak Correlation Id odczytanego komunikatu odpowiedzi MQ. Jeśli aplikacja MQ ustawi Correlation Id odpowiedzi wstawiając w to miejsce message Id zapytania to nod MQGet zwróci odpowiedni komunikat HTTP. Po wyjściu z noda jeśli odczyta on komunikat procesowanie trafi do noda Compute.

W pierwszej części w module znów deklarujemy namespaces używane w WebService

        DECLARE tns NAMESPACE 'http://www.myserver.com.pl/WSPilot/SimpleTypes';
        DECLARE soapenv NAMESPACE 'http://schemas.xmlsoap.org/soap/envelope/';

Następnie w głównej funkcji Main kopiujemy całe drzewo komunikatu, Ustawiamy odpowiednie pola Properties związane z Message Set Odpowiedzi WebService, generujemy treść odpowiedzi na podstawie odczytanego komunikatu odpowiedzi, ustawiamy odpowiednio Identyfikator zapytania WebService, z którym chcemy skorelować odpowiedź oraz usuwamy wszystko co znajduje sie wewnątrz Environment.

        SET OutputRoot.Properties = InputRoot.Properties;

        SET OutputRoot.Properties.MessageSet = 'PHTM4NG002001';
        SET OutputRoot.Properties.MessageType = 'Envelope';
        SET OutputRoot.Properties.MessageFormat = 'XML1';
        SET OutputRoot.MRM.soapenv:Body.tns:addIntResponse.tns:addIntResult.v=InputRoot.XMLNS.RESPONSE.DATA.V;

        SET OutputLocalEnvironment.Destination.HTTP.RequestIdentifier = CAST(Environment.XMLNS.HTTP.RequestIdentifier AS BLOB);
        SET Environment= null;


Następnie przepływ kończymy nodem HTTPReply wysyłającym odpowiedź WebService.


Nody HTTP i SSL

HTTPInput - konfiguracja noda

Parametr noda HTTPInput mówiący o tym, że obsługuje on komunikacje szyfrowaną to Use HTTPS w zakładce Basic.Niestety tylko to nie wystarczy do bezproblemowej obsługi zapytań SSL. Należy jeszcze odpowiednio skonfigurowaćbroker za pomocą komendy mqsichangeproperties. Te operacje to:

Włączenie obsługi SSL

mqsichangeproperties [brokerName] -b httplistener -o HTTPListener -n enableSSLConnector -v true

Ustawienie pliku z bazą certyfikatów

mqsichangeproperties [brokerName] -b httplistener -o HTTPSConnector -n keystoreFile -v [fully qualified file path to keystore file]


Ustawienie hasła do pliku z bazą certyfikatów

mqsichangeproperties [brokerName] -b httplistener -o HTTPSConnector -n keystorePass -v [password for keystore]


Ustawienie portu, na którym nasłuchuje broker na połączenia SSL

mqsichangeproperties [brokerName] -b httplistener -o HTTPSConnector -n port -v [Port to listen on for https]


HTTPRequest - konfiguracja noda

Aby móc komunikować się za pomocą SSL z brokera będąc klientem (HTTPRequest) należy najpierw skonfigurować bazę certyfikatów, tzn. określić jakim certyfikatem broker będzie się przedstawiał oraz jakie certyfikaty są dla niego wiarygodne w tego typu komunikacji (od brokera). Standardowa baza certyfikatów znajduje się w pliku %MQSI_FILEPATH%\jre\lib\security\cacert. Jest to baza w formacie JKS i można ją zarządzać za pomocą standardowego narzędzia JAVA keytool lub z narzędzia okienkowego.Początkowe hasło do tego pliku to changeit i jak sama nazwa wskazuje należy ja jak najszybciej zmienić.


Parametry noda HTTPRequest dotyczące połączenia szyfrowanego znajdują się w zakładce SSL.Znajduje się tam parametr Protocal. Przyjmuje on następujące wartości:

SSL : Używa protokołu SSLv3, a jeśli się nie uda SSLv2
SSLv3 : Używa tylko protokołu SSLv3
TLS : Używa tylko protokołu TLS

Kolejny parametr Allowed SSL Ciphersokreśla sposób kodowania podczas połączenia szyfrowanego. Jeśli tej wartości nie wprowadzimy broker przyjmie wartość domyślną, tj. wszystkie możliwe sposoby akceptowane przez JVM brokera. Dalej należy tylko i wyłącznie w polu Web Service URL podać odpowiedni adres URL zasobu wymagającego szyfrowanej komunikacji (prefix adresu https://).


Manipulowanie treścią komunikatu

W tym miejscu postaram się przedstawić kilka przykładów manipulacji danymi komunikatu.

Parsowanie CDATA

Wyobraźmy sobie sytuacje, że w wejściowym komunikacie mamy dane CDATA, które będziemy chcieli w przepływiesparsować za pomocą odpowiedniego parsera, tak aby odczytać to pole nie jako łańcuch tekstowy, ale jako drzewo XML.Mamy wejściowy XML o poniższej strukturze, gdzie mamy tagi MESSAGE/A, a w nim wartość CDATA zawierający dynamicznąstrukturę XML.

<?xml version="1.0" encoding="UTF-8"?>
<MESSAGE>
        <A><![CDATA[<B>beee</B>]]></A>
</MESSAGE>

Poniższy kod ESQL do drzewa Environment dodaje zmienną mycdata zawierającą drzewo CDATA z poniższego przykładu.

...
CREATE LASTCHILD OF Environment NAME mycdata DOMAIN('XMLNS')
PARSE (InputRoot.XMLNS.MESSAGE.A,InputRoot.Properties.Encoding,
InputRoot.Properties.CodedCharSetId);
...

Określenie częsci wejściowego XML jako wyjściowy XML

Wyobraźmy sobie wejściowy komunikat XML opakowany poprzez SOAP.

<?xml version="1.0" ?>
<soapenv:Envelope xmlns="http://www.abc.com.pl" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
                <message>
                        <data>mydata</data>
                </message>
        </soapenv:Body>
</soapenv:Envelope>

Poniższy kod powoduje w komunikacie wyjściowym usunięcie zewnętrznych tagów SOAP.

        DECLARE cursor REFERENCE TO InputRoot.XMLNS;
        DECLARE firstTag CHARACTER;
        MOVE cursor FIRSTCHILD TYPE 0x01000000;
        MOVE cursor FIRSTCHILD TYPE 0x01000000;
        MOVE cursor FIRSTCHILD TYPE 0x01000000;
        SET firstTag=FIELDNAME(cursor);
        MOVE cursor PARENT;
        SET OutputRoot.XMLNS.{firstTag}=cursor;

W rezultacie otrzymamy na wyjściu poniższy komunikat:

<message>
        <data>mydata</data>
</message>


Referencje

Referencje ESQL to po części odpowiednik wskaźników języka C.Podczas deklaracji informujemy, że dany referencja wskazuje na konkretne miejsce w strukturze drzewa komunikatu.

DECLARE myRef REFERENCE TO InputBody.Data.Invoice;
Set MyChar=FIELDNAME(myRef);
SET myRef.Number=100;
MOVE myRef FIRSTCHILD;
MOVE myRef NEXTSIBLING NAME 'OrderItem';
MOVE myRef LASTCHILD TYPE NameValue;
MOVE myRef PREVIOUSSIBLING REPEAT TYPE;


Trace

W przypadku bardzo szczegółowego badania zachowania brokera można włączyć trace.Trace brokera dzielimy na systemowy (bardziej szczegółowy) i użytkownika. Dodatkowo możemy określić poziom logowania. Trace możemy określić na poziomie całej EG, jak również konkretnego przepływu.

Aby włączyć trace należy użyć komendy mqsichangetrace. Pierwszym argumentem komendy jest nazwa brokera. Kolejne to odpowiednie przełączniki. Ciekawsze z nich to:

-u lub -t : określa, czy włączany trace użytkownika, czy systemowy
-e : określa nazwę Execution Group
-f : określa nazwę przepływu, jeśli chcemy włączyć trace tylko konkretnego przepływu, a nie całego EG
-l : określa poziom logowania (none, normal, debug)
-r : usuwa całą informację o zarejestrowanych informacjach trace

Następną operacją, jaką należy wykonać jest odczytanie informacji trace i zapisanie ich do pliku.Aby tego dokonać należy użyć komendy mqsireadlog. Pierwszym argumentem komendy jest nazwa brokera. Kolejne to odpowiednie przełączniki. Ciekawsze z nich to:

-u lub -t : określa, czy włączany trace użytkownika, czy systemowy
-e : określa nazwę Execution Group
-o : nazwa pliku, do którego zostaną zapisane informacje trace

Następnie jeśli chcemy przeanalizować te logi należy je przekonwertować do bardziej ludzkiej postaci.Aby tego dokonać należy użyć komendy mqsiformatlog. Argumenty tej komendy to:

-i : nazwa pliku wejściowego z surowymi danymi trace
-o : nazwa pliku, do którego zostaną zapisane sformatowane informacje trace


Poniżej przedstawiona jest sekwencja przykładowych operacji jakie należy wykonać aby zgrać trace użytkownika do pliku.

#włączenie trace dla przepływu Flow1 z jednoczesnym wykasowaniem starszych logów
mqsichangetrace BROKER_1 -u -e default -f "Flow1" -l debug -r

# w tym miejscu wykonujemy operacje aby aktywować przepływ Flow1
# następnie za pomocą poniższej operacji wyłączamy trace
mqsichangetrace BROKER_1 -u -e default -f "Flow1" -l none

# zapisujemy trace do pliku
mqsireadlog BROKER_1 -u -e default -f -o /tmp/flowtrace.xml

# formatujemy trace do pliku logów
mqsiformatlog -i /tmp/flowtrace.xml -o /tmp/userflowtrace.txt



Parametry UDP

W czasie programowaia i projektowania pzepływów brokera często pojawia się konieczność sparametryzowania go.Chodzi tu o to aby np. w kodzie ESQL nie wpisywać na stałe wartości zmiennyh, tylko aby moc je definiować podczasdeploymentu. Takie parametry definiuje się na przepływie na zakładce User Defined Properties. Tam wprowadzamy nazwy i wartości parametrów.

Aby użyć takiej zmiennej UDP w kodzie ESQL należy zadeklarować zmienną o identycznej nazwie z klauzlą EXTERNAL

DECLARE username EXTERNAL CHARACTER ;

W takiej sytuacji pod zmienną usename wprowadzana jest wartość zmiennej UDP o nazwie username.

Aby użyć takiej zmiennej UDP w kodzie JAVA należy wywołać metodę getUserDefinedAttributez argumentem będącym nazwą zmiennej UDP.

String username=(String)getUserDefinedAttribute("username");


Nody typu Timer

Nody typu Timeout są nowego rodzaju nodami typu input.Pozwalają one na zainicjowanie przepływu nie poprzez pojawienie się komunikatu w kolejce, pojawienie się żądania HTTP, czy innych standardowych nodów typu input.Nody typu Timeout pozwalają na zainicjowanie przepływu na zasadzie schedulera.W najprostszym przypadku przeplyw jest inicjowany zaraz po uruchomieniu przepływu i następnie co określony na nodzie TimeoutNotification interwał czasowy.

TimeoutControl


Jest to nod, który przyjmuje wejściowy komunikat np. wygenerowany ręcznie w nodzie Compute, dokonuje walidacji struktury komunikatu a następnie zapisuje odpowiedni komunikat w kolejce SYSTEM.BROKER.TIMEOUT.QUEUE.

Taki utworzony komunikat TimeoutRequest ma postać:

<TimeoutRequest>
        <Action>SET | CANCEL</Action>
        <Identifier>Identyfikator</Identifier>
        <StartDate>TODAY | yyyy-mm-dd</StartDate>
        <StartTime>NOW | hh:mm:ss</StartTime>
        <Count>licznik wystąpień</Count>
        <Interval>Interwał w sekundach</Interval>
        <IgnoreMissed>TRUE | FALSE</IgnoreMissed>
        <AllowOverwrite>TRUE | FALSE</AllowOverwrite>
</TimeoutRequest>


gdzie:

Action

Określa, czy notyfikacja jest tworzona, czy usuwana.

IgnoreMissed

Określa, czy notyfikacje Timeout, które powinny zostać wygenerowane, kiedy przepływ jest zatrzymany powinny zostać zignoowane (TRUE), czy też wywołane w momencie ponownego uruchomienia przepływu (FALSE).

AllowOverwrite

Określa, czy ponowne wywołanie komunikatu TimeoutRequest o tym samym identyfikatorze ma nadpisać już istniejący (TRUE), czy też jest to niemoziwei w takeij sytuacji zostanie wygenerowany błąd (FALSE).

TimeoutNotification


Tego typu nod pozwala na wygenerowanie komunikatu typu timeout

LocalEnvironment
(
  (0x01000000):TimeoutRequest = (
    (0x03000000):Action         = 'SET'
    (0x03000000):Identifier     = 'TNIDENTIFER'
    (0x03000000):StartDate      = '2007-02-20'
    (0x03000000):StartTime      = '18:47:36.116'
    (0x03000000):Count          = 2
    (0x03000000):Interval       = 60
    (0x03000000):IgnoreMissed   = TRUE
    (0x03000000):AllowOverwrite = TRUE
  )
)


Wywołanie kodu JAVA z ESQL

W przypadku modułów ESQL istnieje możliwość wywołania metod JAVA prosto z kodu ESQL.Funkcjonalność polega na tym, że poza projektem z przepływami tworzymy projekt JAVA, który wskazujemy jako referencje w projekcie z przepływem. W efekcie w pliku bar poza przepływem deployuje się plik jar powstały z projektu JAVA i zawierający kod, który chcemy z ESQL wywołać. Następnie wewnątrz ESQL deklarujemy zewnętrzną procedurę JAVA, której parametry wejściowe i parametr wyjściowy odpowiadają parametrom kodu Java. Ważne jest, że metoda JAVA, która chcemy wywołać z kodu ESQL musi być typu static.

Przykład procedury ESQL:

CREATE PROCEDURE getRecords (IN cursor REFERENCE, IN queue_in CHARACTER ,IN host CHARACTER, IN port INTEGER, IN database CHARACTER, IN user CHARACTER, IN password CHARACTER)
RETURNS BOOLEAN LANGUAGE JAVA EXTERNAL NAME "dbrouting.DBRouter.getRecords";

Kod Java

public static Boolean getRecords(MbElement tab, String queue_in
                        ,String host
                        ,Long port
                        ,String database, String user, String password
                        )
{
return true;
}



Poniższa lista zawiera mapowanie typów danych pomiędzy ESQL a JAVA


ESQLJAVA INJAVA INOUT i OUT
INTEGERLong Long[]
FLOATDouble Double[]
DECIMALBigDecimal BigDecimal[]
CHARACTERString String[]
BLOBbyte byte[]
BITBitSet BitSet[]
DATEcom.ibm.broker.plugin.MbDate com.ibm.broker.plugin.MbDate[]
TIMEcom.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
GMTTIME        com.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
TIMESTAMPcom.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
GMTTIMESTAMPcom.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
INTERVALBrak Brak
BOOLEANBooleanBoolean[]
REFERENCEcom.ibm.broker.plugin.MbElement com.ibm.broker.plugin.MbElement[]
ROWBrak Brak
LISTBrak Brak



Dostępne parametry brokera

Poniższa lista zawiera możliwe do odczytania z kodu ESQL lub JAVA parametry brokera.


Nazwa parametruOpisDostępne z JAVA
BrokerDataSourceUserId TAK
BrokerDataSource NIE
BrokerName TAK
BrokerUserId NIE
BrokerVersion NIE
ExecutionGroupLabel TAK
ExecutionGroupName NIE
Family NIE
ProcessId NIE
QueueManagerName TAK
WorkPath NIE


Poniższa lista zawiera możliwe do odczytania z kodu ESQL lub JAVA parametry przepływu.


Nazwa parametruOpisDostępne z JAVA
AdditionalInstances NIE
CommitCount NIE
CommitInterval NIE
CoordinatedTransaction TAK
MessageFlowLabel TAK


Przykład

Poniższy przykład pokazuje w jaki sposób pobrać nazwę brokera z kodu ESQL.

...
DECLARE myBrokerName CHARACTER;
SET myBrokerName =BrokerName;
...


Poniższy przykład pokazuje w jaki sposób pobrać nazwę brokera z kodu JAVA.

String brokerName =getBroker().getName();


Modyfikacja parametrów pliku BAR

Bardzo często się zdarza tak, że pewne parametry pliku bar są muszą być modyfikowane.Dzieje się tak na przykład przy uruchamianiu przepływu na różnych środowiskach.Kiedy mamy poza środowiskiem produkcyjnym środowisko deweloperskie, testowe to przed wdrożeniem pliku barmusimy niektóre jego parametry zmienić. Takim parametrem może być np. adres URL nodów HTTP.

Mamy wtedy dwie możliwości. Pierwszą możliwością jest ręczna modyfikacja pliku bar przed jego wdrożeniem.Może to być jednak bardzo uciążliwe, w szczególności, kiedy tych parametrów będzie większa ilość. Drugi minus takiego rozwiązania jest fakt, ze zawsze przy ręcznych modyfikacjach jest większe prawdopodobieństwo popełnienia błędu.

Innym sposobem jest użycie komendy toolkita (od wersji 6.0.2)mqsiapplybaroverride.Komenda przyjmuje następujące parametry:

-b : położenie pliku bar
-p : plik z parametrami do podmienienia

W pliku bar, który jest zwykłych archiwum danych interesuje nas plik /META-INF/broker.xml. Plik ten zawiera m. in. parametry pliku bar.

Aby móc modyfikować jakieś parametry pliku bar najlepiej nadać w naszym oryginalnym pliku wartości parametrów poprzez odpowiedni klucz. Dla przykładu jeśli mamy parametr wskazujący na pole URL noda HTTPRequest, to jako wartość nie podajemy konkretny adres URL, a np. {MYAPP_URL}.Kiedy otworzymy teraz plik bar i w nim /META-INF/broker.xmlto zobaczymy w nim wpis podobny do poniższego:

<ConfigurableProperty override="{MYAPP_URL}" uri="HTTP_ILServices_Client1#HTTP_ILServices.URL"/>

Teraz musimy dla każdego środowiska przygotować odpowiednie pliki properties zawierające docelowe wartościtego klucza oraz innych.Taki plik może mieć postać:

{MYAPP_URL}=http://serverdev.com.pl/services/myservice

Zadaniem komendymqsiapplybaroverridejest modyfikacja pliku bar a konkretnie w nim pliku /META-INF/broker.xmltak, aby zastąpić wszelkie wystąpienia kluczy na ich wartości z pliku properties.Wywołanie takiej komendy może mieć postać:

mqsiapplybaroverride -b MK_DEPLOY.bar -p parameters.properties


CVS runtime versioning i keywords

Przy developmencie przepływów, a następnie wdrażaniu ich na poszczególne środowiska ważne jest, aby wiedzieć dokładnie jaką wersje konkretnego przepływu mamy zainstalowaną. Mam tu na myśli wersje przepływu, kodu ESQL, Message Set, kodu JAVA oraz plików XSD.Oczywiście można ustawiać wszystkie informacje o wersji ręcznie, lecz ma to same wady.Jeśli robimy coś ręcznie zawsze istnieje możliwość, że popełnimy błąd. Dodatkowo jest to pracochłonne.Jeśli używamy jakiegoś systemu zarządzania wersją kodu wskazane jest aby informacja o wersji automatycznie propagowała się na parametr Version, jaki możemy przeczytać np. w opisie przepływu lub Message Set zainstalowanych na brokerze. W tym miejscy postaram się opisać konfiguracje takiego rozwiązania z wykorzystaniem CVS jako narzędzia do zarządzania wersją.

W przypadku używania CVS ważne jest aby plik, w jakim automatycznie będą pojawiały się informacje o wersji były typu -kkv
W rezultacie informacja o wersji danego obiektu pojawia sie jako parametr obiektu (przepływu, Message Set) zainstalowanego na brokerze.

Wersjonowanie przepływu i Message Set

Każdy przepływ ma parametr Version. Aby pole to CVS automatycznie uzupełniał wersją przepływu należy wtym polu wprowadzić wartość$Revision: $. Analogiczny parametr istnieje dla Message Set. Jeśli plik zapisany w CVS jest typu -kkv, to powyższytekst jest uzupełniany przez numer wersji CVS.

Automatyzacja

Aby nie ustawiać za każdym razem pola Version należy w konfiguracji toolkita przejść do menu Preferences, a następnie do zakładki Broker Development i Message Flow Editor i tam w polu Default Version Tag wpisać $Revision: $ Będzie to skutkowało tym, że każdy nowoutworzony przepływ będzie miał na tą wartość ustawione pole Version.Analogicznie robimy to dla Message Set.

Wersjonowanie kodu ESQL

W przypadku kody ESQL najbardziej odpowiednim sposobem jego wersjonowania jest wprowadzenie do kodu wartości $Revision: $ jako komentarza.

Wersjonowanie kodu JAVA

Wersjonowanie plików XSD

Keywords

Innym ciekawym dodatkowym sposobem opisywania zasobów na brokerze jest używanie kluczy.Klucze pozwalają nam opisywać przepływy, czy też Message Set nie tylko przez numer wersji, ale przez dodatkowe informacje. Taką dodatkową informacją może być autor, opis, lub każda inna informacja.Następnie ta informacja pojawi się jako dodatkowe pole opisujące zdeployowany obiekt brokera.

Takie dodatkowe informacje można umieszczać w polu Long Description przepływu, polu Label noda Passthrought lub jako komentarz w module ESQL. Format takiej informacji ma postać:$MQSI klucz = wartość MQSI$Dla przykładu jeśli chcemy umieścić informacje o autorze może to wyglądać następująco:$MQSI Autor = Marcin Kasiński MQSI$


Accounting and Statistics

Accounting and Statistics to technologia pozwalająca na zbieranie informacji statystycznych na temat działaniaprzepływów zainstalowanych na brokerze.
Rozróżniamy dwa sposoby zbierania danych. Pierwszy to statystyki Snapshot zbierane co około 20 sekund.Zazwyczaj używa się ich do analizy konkretnych problemów wydajnościowych. Drugi to statystyki Archive używane zazwyczaj do długotrwałego monitorowania pracy brokera. Częstotliwość zbierania danych typu Archive określamyw minutach poprzez komendę mqsichangebroker. Zebrane dane mogą być zapisywane jako trace typu użytkownika i odczytywane jak standardowe informacje trace lub publikacje na odpowiedni topic.

Przykład ustawienia tego interwału dla brokera WBRK_BROKERna 10 minut wygląda następująco:

mqsichangebroker WBRK_BROKER -v 10

Włączenie i wyłączenie statystyk dokonujemy komendą mqsichangeflowstats. Sprawdzenie poziomu mitorowania dokonujemy komendąmqsireportflowstats

Pierwszym argumentem komendy mqsichangeflowstatsjest nazwa brokera. kolejne to odpowiednie przełączniki:

-a : komenda dotyczy danych typu Archive
lub
-s : komenda dotyczy danych typu Snapshot
-e : nazwa execution group
lub
-g : komenda dotyczy wszystkich execution group
-f : nazwa przepływu
lub
-j : komenda dotyczy wszystkich przepływów
-c : włącza (wartość active) lub wyłącza (wartość inactive) monitorowanie
-t : ustawia poziom logowania dla wątków (wartość none lub basic)
-n : ustawia poziom logowania dla nodów (wartość none, basic lub advanced)
-r : czyści poprzednie wpisy
-o : ustawia docelowe położenie statystyk (wartość usertrace, xml lub smf dla zOS)
-b : parametr służący do filtrowania statystyk według pola accountingOrigin.


Poniżej kilka przykładów użycia komendymqsichangeflowstats.

Włącznie monitorowania typu Archive
mqsichangeflowstats WBRK_BROKER -a -g -j -c active -t basic -r -o usertrace

Wyłącznie monitorowania typu Archive
mqsichangeflowstats WBRK_BROKER -a -g -j -c inactive -t basic -r -o usertrace

Sprawdzenie poziomu monitorowania typu Archive
mqsireportflowstats WBRK_BROKER -a -g -j

Włącznie monitorowania typu Snapshot
mqsichangeflowstats WBRK_BROKER -s -g -j -c active -t basic -o usertrace

Wyłącznie monitorowania typu Snapshot
mqsichangeflowstats WBRK_BROKER -s -g -j -c inactive -t basic -o usertrace

Sprawdzenie poziomu monitorowania typu Snapshot
mqsireportflowstats WBRK_BROKER -s -g -j

Aby dynamicznie określić "domenę" statystyk, według której później będziemy mogli filtrować statystykimożemy użyć poniższego kodu ESQL.

SET Environment.Broker.Accounting.Origin = "CustomerXY";

Poniższa lista przedstawia ogólne dane statystyczne znajdujące się w folderze WMQIStatisticsAccounting.

NazwaTyp danychOpis
RecordTypeCharacter Typ danych (Archive lub Snapshot)
RecordCodeCharacter MajorInterval, Snapshot, Shutdown, ReDeploy lub StatsSettingsModified

Poniższa lista przedstawia dane statystyczne dotyczące przepływów znajdujące się w folderze MessageFlow.

NazwaTyp danychOpis
BrokerLabelCharacter (max 32)Nazwa brokera
BrokerUUIDCharacter (max 32)Identyfikator brokera
ExecutionGroupNameCharacter (max 32)Nazwa EG brokera
ExecutionGroupUUIDCharacter (max 32)Identyfikator EG brokera
MessageFlowNameCharacter (max 32)Nazwa przepływu
StartDateCharacterData startu (YYYY-MM-DD)
StartTimeCharacterCzas startu (HH:MM:SS:NNNNNN)
EndDateCharacterData zakończenia (YYYY-MM-DD)
EndTimeCharacterCzas zakończenia (HH:MM:SS:NNNNNN)
TotalElapsedTimeNumericCałkowity czas spędzony na procesowaniu komunikatów
MaximumElapsedTimeNumericMaksymalny czas spędzony na procesowaniu komunikatów
MinimumElapsedTimeNumericMinimalny czas spędzony na procesowaniu komunikatów
TotalCPUTimeNumericCałkowity czas procesora spędzony na procesowaniu komunikatów
MaximumCPUTimeNumericMaksymalny czas procesora spędzony na procesowaniu komunikatów
MinimumCPUTimeNumericMinimalny czas procesora spędzony na procesowaniu komunikatów
CPUTimeWaitingForInputMessageNumericCałkowity czas procesora spędzony na oczekiwaniu na komunikat
ElapsedTimeWaitingForInputMessageNumericCałkowity czas spędzony na oczekiwaniu na komunikat
TotalInputMessagesNumericIlość przeprocesowanych komunikatów
TotalSizeOfInputMessagesNumericCałkowity rozmiar przeprocesowanych komunikatów
MaximumSizeOfInputMessagesNumericMaksymalny rozmiar przeprocesowanych komunikatów
MinimumSizeOfInputMessagesNumericMinimalny rozmiar przeprocesowanych komunikatów
NumberOfThreadsInPoolNumericIlość wątków w puli
TimesMaximumNumberofThreadsReachedNumericIlość wystapień osiągniecia maksymalnej ilości wątków
TotalNumberOfMQErrorsNumericIlość błedów MQGET lub błedów Web Services
TotalNumberOfMessagesWithErrorsNumericIlość komunikatów zawierających błedy
TotalNumberOfErrorsProcessingMessagesNumericIlość błędów w procesowaniu komunikatów
TotalNumberOfTimeOutsWaitingForRepliesToAggregateMessagesNumericIlość timeoutów w nodzie AggregateReply
TotalNumberOfCommitsNumericIlość operacji commit
TotalNumberOfBackoutsNumericIlość operacji backout
AccountingOriginCharacterAccountingOrigin

Poniższa lista przedstawia dane statystyczne dotyczące wątków znajdujące się w folderze Threads.

NazwaTyp danychOpis
NumberNumericIlość podfolderów zawierających informacje o wątkach

Poniższa lista przedstawia dane statystyczne dotyczące konkretnych wątków znajdujące się w podfolderze ThreadStatisticsfolderuThreads.

NazwaTyp danychOpis
NumberNumericNumer wątku w puli
TotalNumberOfInputMessagesNumericIlość komunikatów przeprocesowanych przez wątek
TotalElapsedTimeNumericCałkowity czas spędzony na procesowaniu komunikatów
TotalCUPTimeNumericcałkowity czas procesora spędzony na procesowaniu komunikatów
CPUTimeWaitingForInputMessageNumericCałkowity czas procesora spędzony na oczekiwaniu na komunikat
ElapsedTimeWaitingForInputMessageNumericCałkowity czas spędzony na oczekiwaniu na komunikat
TotalSizeOfInputMessagesNumericCałkowity rozmiar przeprocesowanych komunikatów
MaximumSizeOfInputMessagesNumericMaksymalny rozmiar przeprocesowanych komunikatów
MinimumSizeOfInputMessagesNumericMinimalny rozmiar przeprocesowanych komunikatów

Poniższa lista przedstawia dane statystyczne dotyczące nodów znajdujące się w folderze Nodes.

NazwaTyp danychOpis
NumberNumericIlość podfolderów zawierających informacje o nodach

Poniższa lista przedstawia dane statystyczne dotyczące konkretnych nodów znajdujące się w podfolderze NodesStatisticsfolderuNodes.

NazwaTyp danychOpis
LabelCharacterNazwa noda
TypeCharacterTyp noda
TotalElapsedTimeNumericCałkowity czas spędzony na procesowaniu komunikatów
MaximumElapsedTimeNumericMaksymalny czas spędzony na procesowaniu komunikatów
MinimumElapsedTimeNumericMinimalny czas spędzony na procesowaniu komunikatów
TotalCPUTimeNumericCałkowity czas procesora spędzony na procesowaniu komunikatów
MaximumCPUTimeNumericMaksymalny czas procesora spędzony na procesowaniu komunikatów
MinimumCPUTimeNumericMinimalny czas procesora spędzony na procesowaniu komunikatów
CountOfInvocationsNumericCałkowita ilośc komunikatów przeprocesowanych przez ten nod
NumberOfInputTerminalsNumericIlość terminali wejściowych
NumberOfOutputTerminalsNumericIlość terminali wyjściowych

Poniższa lista przedstawia dane statystyczne dotyczące terminali znajdujące się w folderze TerminalStatistics.

NazwaTyp danychOpis
LabelCharacterNazwa terminala
TypeCharacterTyp terminala (Input lub Output)
CountOfInvocationsNumberCałkowita ilość komunikatów przeprocesowanych przez ten terminal


Configuration Manager Proxy

Wyświetlanie listy brokerów

        private static void displayBrokerNames(ConfigManagerProxy cmp)
        {
        try
                {
                TopologyProxy topology = cmp.getTopology();
                if (topology != null)
                        {
                        Enumeration allBrokers = topology.getBrokers(null);
                                while (allBrokers.hasMoreElements())
                                {
                                        BrokerProxy thisBroker = (BrokerProxy) allBrokers.nextElement();
                                        System.out.println("Broker "+thisBroker.getName());
                                } //while
                        } //if
                } //try
        catch(ConfigManagerProxyPropertyNotInitializedException ex)
        {
        System.err.println("Comms problem! "+ex);
        }
        
        }        

Usuwa broker

        private static void deleteBroker(ConfigManagerProxy cmp, String brokerName) {
                try {
                        TopologyProxy topology = cmp.getTopology();
                        if (topology != null) {
                                topology.deleteBroker(brokerName);

                        } //if
                } //try
                catch (ConfigManagerProxyException ex) {
                        System.err.println("Problem! " + ex);
                }
        }

Wyświetlanie listy EG i ich przepływów i MRMów

        private static void displayBrokerEGs(ConfigManagerProxy cmp,
                        String brokerName) {
                try {
                        TopologyProxy topology = cmp.getTopology();
                        if (topology != null) {
                                BrokerProxy broker = topology.getBrokerByName(brokerName);

                                Enumeration executiongroups = broker.getExecutionGroups(null);

                                while (executiongroups.hasMoreElements()) {
                                        ExecutionGroupProxy executiongroup = (ExecutionGroupProxy) executiongroups
                                                        .nextElement();
                                        System.out.println("EG-> " + executiongroup.getName());

                                        Enumeration deployedObjects = executiongroup
                                                        .getDeployedObjects();

                                        while (deployedObjects.hasMoreElements()) {
                                                DeployedObject deployedObject = (DeployedObject) deployedObjects
                                                                .nextElement();
                                                System.out.println(deployedObject.getName() + " "
                                                                + deployedObject.getFullName());
                                        } //while
                                } //while
                        } //if
                } //try
                catch (ConfigManagerProxyPropertyNotInitializedException ex) {
                        System.err.println("Comms problem! " + ex);
                }

        }

Wyświetlanie listy ACLi

        private static void displayACLs(ConfigManagerProxy cmp)
        {
        AccessControlEntry[] ace = cmp.getAccessControlEntries();
                for (int i=0; i<ace.length; i++)
                {
                System.out.println(ace[i].getType() + " " + ace[i].getName() +
                " has " + ace[i].getPermission());
                }
        }

Dodanie ACL

        private static void createACL(ConfigManagerProxy cmp, String user) {
                AccessControlEntry acls[] = new AccessControlEntry[1];

                AccessControlEntry acl = new AccessControlEntry(user,
                                AccessControlEntryPrincipalType.user,
                                AccessControlEntryPermission.fullControl);

                acls[0] = acl;

                try {
                        cmp.addAccessControlEntries(acls);
                } catch (ConfigManagerProxyLoggedException e) {
                
                        e.printStackTrace();
                }
        }

Wyświetlanie listy subskrypcji

        private static void displaySubs(ConfigManagerProxy cmp) throws Exception
        {
                String topics = null;
                // all topics
                //String brokers = "BROKER%";
                String brokers = null;
                // all brokers beginning 'BROKER'
                String users = null;
                // all users
                String subsPoints = null;
                // all subscriptions points
                GregorianCalendar start = null;
                // no start date
                GregorianCalendar end = new GregorianCalendar();
                // to the present
                SubscriptionsProxy s = cmp.getSubscriptions(topics, brokers, users, subsPoints, start, end);
                Enumeration elements=s.elements();
                for (Enumeration e = s.elements() ; e.hasMoreElements() ;)
                {
                        System.out.println(e.nextElement());
                }
        }


Literatura

"WebSphere Message Broker - Message Flows" : IBM
"WebSphere Message Broker - ESQL" : IBM
"WebSphere Message Broker - Configuration, Administration, and Security" : IBM
"WebSphere Message Broker" : IBM Info Center

Powrót