Přeskočit na hlavní obsah

Posts

2012


Java a fault handling policies v Oracle SOA Suite

·6 min
Jednou ze součástí Oracle SOA Suite je tzv. Fault Management Framework, který se mmj. stará o zpracování výjimek v BPELu. Pokud během invoke aktivity nastane výjimka, framework ji odchytí a předá ji ke zpracování akci, která je definovaná ve fault policy.

Tyto politiky jsou zajímavou alternativou k odchytávání výjimek v samotném BPELu  pomocí fault handleru <catch>. Dá se na ně pohlížet jako na aspekt (ve smyslu AOP), který je deklarativně navěšený na procesu (nebo i celé kompozitní aplikaci).

No, pokud jsem řekl aspekt, pravděpodobně jsem vzbudil velká očekávání. Tak to bohužel není - sám jsem musel pohřbít některé své designové představy - má to spoustu omezení, resp. možnosti nejsou rozsáhlé. Takže co to vlastně umí?

V rámci politiky se dají definovat následující zotavné akce:
  • Retry. Nepovedený invoke se dá zopakovat. Dá se nastavit počet opakování, prodleva, exponenciální prodleva a následující zřetězená akce, pokud retry nezafunguje.
  • Human Intervention. Proces se zastaví a je možné ho manuálně obnovit z management konzole.
  • Terminate Process. Proces je ukončen. Totéž jako aktivita.exit.
  • Rethrow Fault. Chyba je vyhozena zpátky do BPEL procesu.
  • Replay Scope. Vyhodí reply fault, což způsobí znovu vykonání aktivit ve scope.
  • Java Code. Zavolá externí Java třídu. To je to, na co se budeme dále soustředit.

Problém

Mám pro klienty vystavenou službu SOAPFault, která má ve svém kontraktu definovaný SOAP Fault:
<xsd:element name="fault">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="resultCode"
type="xsd:string"/>
<xsd:element name="error">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="errorCode"
type="xsd:string"/>
<xsd:element name="errorDescription"
type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

Pro graficky orientované to vypadá takhle:


Tento fault je pak použit ve WSDL:
<wsdl:message name="faultMessage">
<wsdl:part name="payload" element="inp1:fault"/>
</wsdl:message>

<wsdl:portType name="SOAPFaultPort">
<wsdl:operation name="test">
<wsdl:input message="tns:requestMessage"/>
<wsdl:output message="tns:replyMessage"/>
<wsdl:fault name="testfault"
message="tns:faultMessage"/>
</wsdl:operation>
</wsdl:portType>
Služba je implementovaná pomocí BPEL procesu, který v rámci orchestrace volá další službu, nazvanou BPELFault:


Tak, to byla expozice. Teď přijde kolize. V BPELu mám definovanou invoke aktivitu, která volá externí službu BPELFault. Chtěl bych použít politiku tak, aby když externí služba vrátí chybu, aby mi politika nastavila můj definovaný SOAP fault a proces ho vrátil klientovi.

Než se pustíme do implementace politiky, musíme v BPEL procesu ještě splnit dvě podmínky. Jednak v procesu definovat proměnnou, která bude mít nastavený typ zprávy jako daný fault:
<variable name="soapFault"
messageType="ns1:faultMessage"/>
A druhak, proces musí náš fault vyhodit ven pomocí aktivity throw:
<catchAll>
<throw name="ThrowFault"
faultName="ns1:testfault"
faultVariable="soapFault"/>
</catchAll>

Jak to celé funguje?

Obrázek je za tisíc slov, takže tady je BPMN diagram. Swimliny jsou trochu nečitelný :-/ takže odshora: BPEL, External WS, Fault Management Framework a Java Class.


Java

Java třída, která bude z politiky volaná musí implementovat rohraní IFaultRecoveryJavaClass. To má dvě metody, nás bude zajímat pouze handleFault, která má jako parametr IFaultRecoveryContext. Pomocí tohoto kontextu lze přistupovat k objektům v BPEL procesu, odkud vyletěla výjimka.

Z kontextu si tak vytáhneme výše uvedenou BPEL proměnnou soapFault a pomocí XPath, nebo DOMu ji naplníme. Na závěr vrátíme z metody string RETHROW, aby politika vrátila řízení zpátky do BPEL procesu, odkud je pak už vrácena SOAP fault klientovi.

(Omlouvám se, že ten kód uvádím celý. Ale kdyby náhodou to někdo (v Česku) řešil, tak ať to má trochu jednodušší :-)
package cz.swsamuraj.soa.fault;

import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;

import
oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import
oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
* This class is called by fault policy. Its purpose is to set up
* a variable which is then throwed as a SOAP fault.
*
* @author Guido
*/
public class SOAPFaultHandler implements IFaultRecoveryJavaClass {

private static final String VARIABLE = "soapFault";
private static final String PART = "payload";
private static final String RESULT_CODE_NAME = "resultCode";
private static final String RESULT_CODE_VALUE = "255";
private static final String ERROR_NAME = "error";
private static final String ERROR_CODE_NAME = "errorCode";
private static final String ERROR_CODE_VALUE =
"EXTERNAL_SERVICE_ERROR";
private static final String ERROR_DESCRIPTION_NAME =
"errorDescription";
private static final String RESULT = "RETHROW";

@Override
public void handleRetrySuccess(
IFaultRecoveryContext iFaultRecoveryContext) {
}

/**
* Method for set up a BPEL variable for SOAP fault.
*
* @param recoveryContext
* @return
*/
@Override
public String handleFault(IFaultRecoveryContext recoveryContext) {
if (recoveryContext instanceof BPELFaultRecoveryContextImpl) {
BPELFaultRecoveryContextImpl bpelContext =
(BPELFaultRecoveryContextImpl) recoveryContext;

Element root =(Element)
bpelContext.getVariableData(VARIABLE,
PART, "/");
Node soapFault = root.getFirstChild();
String namespaceURI = soapFault.getNamespaceURI();

bpelContext.addAuditTrailEntry(String.format(
"Obtained a root element: {%s}%s.", namespaceURI,
soapFault.getLocalName()));

// removes all child nodes
if (soapFault.hasChildNodes()) {
int count = soapFault.getChildNodes().getLength();

for (int i = 0; i < count; i++) {
Node child = soapFault.getFirstChild();
soapFault.removeChild(child);
}
}

Document document = soapFault.getOwnerDocument();
Node resultCode = createNode(document, namespaceURI,
RESULT_CODE_NAME, RESULT_CODE_VALUE);
soapFault.appendChild(resultCode);

Node error = createNode(document, namespaceURI,
ERROR_NAME, null);
soapFault.appendChild(error);

Node errorCode = createNode(document, namespaceURI,
ERROR_CODE_NAME,
ERROR_CODE_VALUE);
error.appendChild(errorCode);

Node errorDescription = createNode(document, namespaceURI,
ERROR_DESCRIPTION_NAME,
bpelContext.getFault()
.toString());
error.appendChild(errorDescription);

bpelContext.addAuditTrailEntry(String.format(
"SOAP fault response has been set"
+ " by fault policy '%s'.",
bpelContext.getPolicyId()));
}

return RESULT;
}

/**
* Method creates a new {@link Node} with given parameters.
*
* @param document {@link Document}
* @param qualifiedName name of the element
* @param namespaceURI namespace of the element
* @return {@link Node}
*/
private Node createNode(Document document, String namespaceURI,
String qualifiedName, String textContent) {
Node node = document.createElementNS(namespaceURI,
qualifiedName);

if (textContent != null) {
node.setTextContent(textContent);
}

return node;
}
}

Zajímavá je zde asi pouze metoda kontextu addAuditTrailEntry, která propisuje informace do auditního záznamu procesu, což je pak vidět v konzoli Enterprise Managera:


Definice politiky

Už máme ruce, teď mozek. Aby politika byla funkční, je potřeba vytvořit dva soubory. Jednak samotnou definici, soubor fault-policies.xml a potom navázání politiky na konkrétní kompozitiní aplikaci (nebo komponent), soubor fault-bindings.xml. Toto jsou defaultní názvy souborů. Pokud chceme jiné názvy, např. protože chceme politiky verzovat, je potřeba tuto odlišnost uvést v definici kompozitní aplikace.
<property name="oracle.composite.faultPolicyFile">
fault-policies-v1.0.xml</property>
<property name="oracle.composite.faultBindingFile">
fault-bindings-v1.0.xml</property>
Nebo graficky:


V definičním souboru fault-policies-v1.0.xml říkáme, že chceme odchytávat výjimku remoteFault (není to standardní chyba definovaná BPELem, ale Oracle BPEL Extension) a chceme, aby byla zpracovaná naší Java třídou:
<?xml version='1.0' encoding='UTF-8'?>
<faultPolicies xmlns="http://schemas.oracle.com/bpel/faultpolicy">
<faultPolicy version="2.0.1" id="BpelInvokeActivityFaults-v1.0">
<Conditions>
<faultName
xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
name="bpelx:remoteFault">
<condition>
<action ref="ora-java"/>
</condition>
</faultName>
</Conditions>
<Actions>
<Action id="ora-java">
<javaAction
className="cz.swsamuraj.soa.fault.SOAPFaultHandler"
defaultAction="ora-rethrow">
<returnValue value="RETRHOW" ref="ora-rethrow"/>
</javaAction>
</Action>
<Action id="ora-rethrow">
<rethrowFault/>
</Action>
</Actions>
</faultPolicy>
</faultPolicies>
Ve vazebním souboru fault-bindings-v1.0.xml jenom říkáme, že naše politika BpelInvokeActivityFaults-v1.0 je svázaná s celou kompozitní aplikací:
<?xml version='1.0' encoding='UTF-8'?>
<faultPolicyBindings
version="2.0.1"
xmlns="http://schemas.oracle.com/bpel/faultpolicy">
<composite faultPolicy="BpelInvokeActivityFaults-v1.0"/>
</faultPolicyBindings>

Deployment do runtime

Pokud máme třídu i politické :-) soubory přímo v kompozitní aplikaci, nemusíme deployment nijak řešit - prostě kompozitní aplikaci standardně nasadíme. Pokud však chceme politiky externalizovat (protože je chceme přepoužít pro více kompozitek), je vhodné umístit XML soubory do MDS (Metadata Services repository) a Java třídu nasadit do runtimu SOA Suity:
  1. Zkompilovanou třídu zabalíme do JAR souboru.
  2. JAR soubor nakopírujeme do adresáře <ORACLE_HOME>/Oracle_SOA1/soa/modules/oracle.soa.ext_11.1.1
  3. V tomto adresáři spustíme příkaz ant.
  4. Restartujeme WebLogic.

Související články


Odkazy

SOA Patterns, kniha

·2 min
SOA je široké téma a kde kdo si do něj schová kde jakou webovou službičku. Určitě proto není na škodu se občas trochu ochytřit a zjistit, "jak se to správně dělá". Jednou z knižních alternativ, po které se dá sáhnout je počin mého oblíbeného vydavatelství Manning, které čersvě vydalo knihu SOA Patterns jejímž autorem je Arnon Rotem-Gal-Oz.

Hned na úvod je potřeba říct, že titul knihy je zavádějící. Daleko přesnější by byl název "Distributed Systems Patterns" s podtitulem "Taking SOA into Account". Nicméně kniha rozhnodně stojí za přečtení a je vhodným doplňkem k jiným "patternovým" knihám. Pokud byste chtěli opravdu SOA patterny, je pravděpodobně jediným dostupným titulem na trhu SOA Design Patterns od Thomase Erla.

Co v knize najdeme? Kniha sleduje klasické schéma návrhových vzorů, takže každý vzor je popsání pomocí sekcí: problém, řešení, technologické mapování a atributy kvality. Vzory (celkem jich je 26) jsou pak seskupeny do jednotlivých kapitol:
  • Foundation structural patterns
  • Patterns for performance, scalability and availability
  • Security and manageability patterns
  • Message exchange patterns
  • Service consumer patterns
  • Service integration patterns

Následují kapitoly s anti-patterny, case study a úplným závěrem je zajímavá kapitola, kde se SOA srovnává s jinými architekturními styly: REST, Cloud computing a Big data.

Jak už jsem psal, SOA je široké téma, takže kniha jej pokrývá pouze z části. Vzhledem k tomu, že titul vyšel u Manningu, tak nepřekvapí, že většina vzorů je "nízkoúrovňových" tzn., že je využijete, pokud bude servisní architekturu implementova from-the-scratch. Tomu odpovídají i příklady uváděné z autorovy praxe - systém pro námořní hlídkování, vyhledávač videí apod., čili dosti specifické systémy, které vyžadují dobrou performance a těžko by se na ně aplikovalo nějaké krabicové řešení.

Pokud ovšem sáhnete po nějaké hotovém/předpřipraveném řešení (a nemusí to být zrovna nějaké dělo od Oracle nebo IBM, ale klidně třeba Apache Camel nebo ServiceMix), tak už tam většina vzorů bude naimplementovaná "pod kapotou". Ovšem samozřejmě, že neškodí vědět, jak to tam sviští.

DOM, Java a odstranění child nodes

·2 min
Dneska to bude jen takový krátký. Řešil jsem nějaký problém, v rámci kterého se pracovalo s DOMem, do kterého jsem potřeboval přidat nějaké nody. Jak jsem průběžně zjistil, někdy mi do metody přicházel dokument, který už ty nody někdy obsahoval (a někdy ne).

Jako čistý řešení mi přišlo dané nody (včechno to byly child elementy jednoho nodu), pokud tam jsou, odstranit a pak je přidat s potřebnými hodnotami. K tomu jsem se rozhodl vzhledem k API, jež DOM v Javě nabízí - které mi tedy moc použitelné nepřijde.

Dokument, o kterém mluvím má (zjednodušeně) takovouhle strukturu:
<root>
<child1/>
<child2/>
<child3/>
</root>
Obecně jsem čekal, že <root> element bude mít nějakou metodu, která mi vrátí kolekci child nodů. Taková metoda sice existuje - getChildNodes() - ale bohužel vrací interface NodeList:
package org.w3c.dom;

public interface NodeList {
public Node item(int index);

public int getLength();
}
Nevím jak vám, ale mě kolekce s takovým rozhraním přijde dost hrozná.

A teď, proč o tom vlastně píšu. Řekl jsem si, OK, získám NodeList, projdu ho v cyklu a jednotlivé nody odstraním. Takže jsem napsal něco takového:
if (root.hasChildNodes()) {
NodeList children = root.getChildNodes();
int count = children.getLength();

for (int i = 0; i < count; i++) {
Node child = children.item(i);
root.removeChild(child);
}
}
Načež mě překvapilo, že mi to vyhazuje NullPointerException. Ono totiž, když se odstraní element z DOMu, tak se odstraní i z daného NodeListu. Divný, ale budiž. Řešením by tedy bylo
Node child = children.item(0);
ale to se mi designově nelíbilo, takže jsem skončil s tímhle kódem:
if (root.hasChildNodes()) {
int count = root.getChildNodes().getLength();

for (int i = 0; i < count; i++) {
Node child = root.getFirstChild();

root.removeChild(child);
}
}
A jak byste to řešili vy? Mě by se líbilo nějaké elegantní řešení.

Custom XSLT funkce v Oracle SOA Suite

·3 min
Už jste se s tím určitě setkali - nejednoznačnost globální definice boolean. (Globální myslím v rámci SOA napříč všemi systémy.) V každém jazyku je to definované trochu jinak. A proto pokud překládám hodnotu z jednoho systému (nebo rozhraní) na jiný, musím použít nějakou konverzi.

Obecně v tomto problému hodně pomůže kanonický datový model (Cannonical Data Model, CDM), ale pořád se nevyhneme tomu překladu (jenom jich bude méně).

Co jsem teď aktuálně řešil (a že nás to na projektu už nějakou chvilku občas drobně trápí), byl překlad boolean typu z XSD na typ v Oracle databázi. Jak asi víte, Oracle DB nemá typ boolean a nejčastěji se to řeší jako integer 0/1, nebo char Y/N apod. Když tak mě v komentářích opravte - nejsem Oraclista (= databázista).

V XSD je to sice jednoznačnější (viz specifikace boolean), ale záleží, jestli pracujeme s kanonickou (true, false), nebo lexikální (true, false, 1, 0) reprezentací.

Protože se tenhle problém čas od času opakoval, rozhodl jsem se napsat uživatelsky definovanou XSLT funkci pro překlad boolean na integer. Platforma, na které se pohybuji je Oracle SOA Suite.

Definice funkce

Protože celá(?) implementace Oracle SOA Suite je v Javě, tak nepřekvapí, že uživatelská XSLT funkce je taky v Javě:
public class CustomFunctions {

public static int booleanToInt(String bool) {
int result = 0;

if ("true".equals(bool) || "1".equals(bool)) {
result = 1;
}

return result;
}

}
Jsou tam dvě drobná omezení. (A) Metoda musí být definovaná jako statická. (B) Jak návratová hodnota, tak parametry metody musí být pouze následujících typů:
  • java.lang.String
  • int
  • float
  • double
  • boolean
  • oracle.xml.parser.v2.XMLNodeList
  • oracle.xml.parser.v2.XMLDocumentFragment

Ve výčtu je sice uvedný boolean, ale jak jsem zjistil, v rámci XSLT transformace vstupuje do metody String. Proto je v uvedené metodě v parametru String a ne boolean.

Dále potřebujeme mapovací konfigurační soubor. Musí mít následující jméno a umístění:
META-INF/ext-mapper-xpath-functions-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<soa-xpath-functions version="11.1.1"
xmlns="http://xmlns.oracle.com/soa/config/xpath"
xmlns:sws="http://www.oracle.com/XSL/Transform/java/cz.swsamuraj.xslt.CustomFunctions">
<function name="sws:booleanToInt">
<className>
cz.swsamuraj.xslt.CustomFunctions
</className>
<return type="number"/>
<params>
<param name="boolean" type="string"/>
</params>
<desc>Method transforms a boolean value to
integer. True is converted to 1 and false
to 0.
</desc>
<detail>Method transforms a boolean value to
integer. True is converted to 1 and false
to 0.
</detail>
<icon>cz/swsamuraj/xslt/Boolean.png</icon>
</function>
</soa-xpath-functions>
Tady je důležité, aby konfigurační soubor obsahoval namespace, který splňuje následující podmínky. Začíná:
http://www.oracle.com/XSL/Transform/java/

a končí plně kvalifikováným názvem Java třídy, tj.:
cz.swsamuraj.xslt.CustomFunctions

Zbytek je předpokládám zřejmý. Pak už stačí zkompilovat třídu, mapovací soubor a ev. resources (ikona) do JAR souboru.



Nastavení IDE

Aby se dala nová funkce používat v IDE (povinný JDeveloper) je potřeba ji tam zaregistrovat:
  1. Tools -> Preferences -> SOA
  2. Tlačítkem Add přidat připravený JAR soubor.
  3. Restartovat JDeveloper.
Pak již stačí otevřít nějaký XSLT soubor a naši novou funkci najdeme v Component Palette pod položkou User Defined:



Deployment do runtime

Nyní již můžeme novou funkci v IDE vyzkoušet (a otestovat), ale aby fungovala také ve službách je potřeba výše vytvořený JAR soubor dostat také na server (SOA Suita běží na WebLogicu):
  1. JAR soubor nakopírujeme do adresáře <ORACLE_HOME>/Oracle_SOA1/soa/modules/oracle.soa.ext_11.1.1
  2. V tomto adresáři spustíme příkaz ant.
  3. Restartujeme WebLogic.

Odkazy

Technologické blogování

·3 min
Recenze knihy o technologickém (odborném) blogování. Aneb jak se z neznámého, katatonického bloggera stane celosvětová hvězda softwarového světa, vydělávající miliony dolarů. 😎

Životní cyklus webových služeb

·5 min
Jedním z aspektů SOA governance, který by se měl zvážit, pokud začneme s verzováním služeb, je definování a správa životního cyklu služeb (service lifecycle). Podobně jako se u vystavených rozhraní služeb snažíme, aby jejich změny byly pro uživatele předvídatelné a srozumitelné (k čemuž nám pomůže snaha o zpětnou kompatibilitu a verzování), měli bychom se podobně snažit o srozumitelnost a předvídatelnost ohledně (časové) dostupnosti služby samotné.

SOA jako taková, je poměrně volné téma, takže každý dodavatel řešení k ní přistupuje po svém (a většinou si ji ohýbá, podle svého technologického a business portofolia. Výjimkou je v tomto většinou open source, který se snaží implementovat podle návrhových vzorů a nemá přitom vedlejší úmysly). Podobně kreativní jsou výrobci i v oblasti životního cyklu SOA služeb. Pokud bysme chtěli vědět, jak vypadá nějaký "obecný" lifecycle, tak brzy zjistíme, že žádný takový není.

Service Lifecycle podle ITIL
Obecně má lifecycle nějaké hlavní fáze, rozdělené do dílčích fází. Např. Oracle má hlavní fáze pouze dvě (design a runtime), ITIL má tři (design, transition a operation) a IBM čtyři (model, assemble, deploy a manage).

Service Lifecycle podle IBM (zdroj IBM)
Pokud bychom si však přesto chtěli takový obecný lifecycle načrtnout, mohlo by to vypadat nějak takto: Na počátku je vždycky požadavek na novou business funkčnost, což by mělo vést k analýze - jestli může požadavek pokrýt nějaká stávající služba/y (nebo jejich kompozice), jestli bude potřeba stávající službu upravit (pak se musí udělat dopadová analýza - impact analysis), nebo jestli vznikne úplně nová služba. Když jsou tyto věci jasný, může se služba zaplánovat (např. kdy se nasadí na produkci).

Service Lifecycle podle Oracle (zdroj Oracle)
Následuje klasický vývojový cyklus, tj. analýza, vývoj a testování služby, nasazení na různá prostředí a pak se služba dostane do produkční fáze - začnou ji používat klienti. Časem se může stát, že služba už nebude splňovat požadavky na ni kladené, změní se požadavky a služba už nebude potřeba, anebo vznikne nová verze služby. Je čas, aby služba odešla do důchodu.

Skutečná podoba lifecyclu se ale bude vždycky odvíjet od procesu, který daná organizace má (nebo by měla mít) pro správu a řízení IT artefaktů. Protože SOA governance je pořád jen podmnožina (a rozšíření) IT governance.

Pokud máme vydefinovaný životní cyklus služby, naskýtá se otázka, kde jej spravovat. Ano, nabízejí se jednoduchá a přímočará řešení - už jsem viděl případy, kdy byla governance služeb držená v Excelu, nebo ji tvořily tři(!) tabulky v databázi. Pokud se však nechceme vydat touto svůdnou cestou a nechceme znovu vymýšlet kolo, je vhodné použít nějakou SOA governance repository.

Toto bývá dost často kamenem úrazu celé SOA governance, protože komerční repository jsou častokrát dražší, než celé zbývající SOA řešení. Pokud však nemáme velké oči (ještě nikdy jsem neviděl SOA governance implementovanou v celé své šíři), dají se, se stejným úspěchem, využít i open source řešení.

Například my jsme na projektu použili WSO2 Governace Registry, profesionální open source řešení, které (prozatím) splňuje naše požadavky. Ke každé registrované (a spravované službě) se dá přiřadit nějaký lifecycle (každá služba může mít jiný) a nastavit konkrétní fázi.


Lifecycle, který jsem pro aktuální projekt adaptoval vypadá následovně. Životní cyklus se skládá z několika stavů, kdy každý z nich může mít několik požadavků, které musí být splněny, aby se služba mohla posunout do dalšího stavu. Cyklus je znázorněn pomocí UML state diagramu, jednotlivé požadavky jsou zobrazeny jako responsibilities:

  • Initial: Začátek lifecyclu, resp. procesu pro správu a životní cyklus služby. U nás takový proces de facto chybí a je nahrazen požadavky jednotlivých projektů. To je samozřejmě z hlediska SOA governance špatně - služba totiž nepřestane existovat jen proto, že je nasazena na produkci a je akceptována (což je pohled projektu).
  • Development: Tady pro nás začíná design fáze lifecyclu (na obrázku zeleně).
  • Contract created: bylo vytvořeno WSDL a bylo akceptováno konzumenty.
  • Implemented: služba byla plně vyvinuta a nasazena na prostředí, kde ji můžou (integračně) otestovat klienti.
  • Documented: služba byla zdokumentována. Pro nás to znamená, že byl jednak plně okomentáván kontrakt (elementy v XSD a služba a operace ve WSDL) a jednak byla zdokumentována orchestrace a externí komunikace služby pomocí CASE nástroje.
  • In Test: Služba byla nasazena na testovací prostředí. Není explicitně řečeno jaké (FAT/SIT/UAT), protože jednotlivé požadavky se týkají různých prostředí.
  • Smoke test passed: release manager ověřil, že služba po nasazení na SIT prostředí "žije".
  • Test cases passed: test manager nechal projít všechny test casy a schválil posunutí služby do UAT testů.
  • Accepted: služba byla akceptována v rámci UAT testů.
  • Available: služba byla na sazena na produkční prostředí a je tak k dispozici konzumentům. Zde začíná run-time fáze cyklu (oranžově).
  • Deprecation notification: konzumenti služby byli notifikování, že služba k nějakému datu přejde do stavu deprecated.
  • Deprecated: služba je označena jako deprecated (určena k penzionování), nicméně stále je ještě k dispozici konzumentům. Pokud je služba označena jako deprecated, je zpravidla již k dispozici její novější verze (jejíž nasazení by mělo být synchronizováno s tímto stavem). Výjimkou může být, pokud je služba rušena bez náhrady - v tom případě je služba označena jako deprecated, ale není nasazena její novější verze.
  • Retirement notification: konzumenti služby byli notifikováni, že služba k nějakému datu přejde do stavu retired.
  • Retired: služba byla penzionovaná - stažena z používání a je klientům nedostupná. Služba v tomto stavu by stále měla být evidovaná v SOA governance repository. Například proto, aby bylo dohledatelné, v jakém časovém rozmezí byla služba aktivní apod.

Na závěr ještě malá ukázka, jak vypadá část lifecyclu v nástroji WSO2 Governace Registry:


    U služby je vždy zobrazen aktuální stav cyklu, požadavky stavu jsou prezentovány checkboxy. Pokud služba splňuje podmínky přechodu do dalšího stavu (a tento stav skutečně již nastal), lze službu posunout do dalšího stavu tlačítkem Promote. Přechody mezi stavy a splnění požadavků lze svázat s formální kontrolou (např. kontrola verzí závislých artefaktů) a konkrétní uživatelskou rolí.

    Související články:

    Verzování XSD v SOAP webových službách

    ·3 min
    Když jsem psal minule o verzování SOAP webových služeb, zaměřil jsem se pouze na verzování v rámci WSDL. Letmo jsem také popsal WSDL strukturu, s tím, že jsem nijak neřešil definici datových typů. Tady se může skrývat další úskalí (nebo příležitost pro) verzování.

    Datové typy jsou uvedeny v elementu types. Pokud není definice typů triviální, nebo pokud není ve službě definovaná pouze jedna operace, tak se zpravidla drží definice externě v XSD souboru a do WSDL se pouze naincludují (pokud chceme mít typy ve stejném namespace, jako má WSDL), nebo naimportují (pokud chceme, aby XSD mělo samostatný namespace).

    Myslím, že je lepší používat element import. Kromě toho, že je to podmínka, aby kontrakt splňoval WS-I Basic Profile, tak to umožňuje dodržovat následující konvenci:
    1. Součástí namespace WSDL je název služby.
    2. Každá operace má svoje vlastní XSD (v němž je definovaný request a response).
    3. Součástí namespace XSD je název služby a název operace.
    4. Plus samozřejmě verzujeme.
    Takže pokud bysme měli službu s názvem MyService a operaci s názvem myOperation, tak by namespacy vypadaly takto:

    WSDL:
    http://sw-samuraj.blog/ws/MyService-v1

    XSD:
    http://sw-samuraj.blog/ws/MyService-v1/myOperation

    Podobně jako u WSDL, je v namespace uvedena pouze major verze a jelikož je už ji obsahuje název služby (MyService-v1), není potřeba ji duplikovat ještě v názvu operace.

    Kromě namespaců verzujeme také samotný XSD soubor, např. myOperation-v1.2.xsd. Což nás staví před následující dilema. Představme si, že máme službu, která obsahuje dvě operace. Něco jako:
    <?xml version="1.0" encoding="UTF-8"?>
    <definitions
    targetNamespace=
    "http://sw-samuraj.blog/ws/MyService-v1"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <types>
    <xsd:schema>
    <xsd:import
    namespace=
    "<trimmed>/MyService-v1/myOperation"
    schemaLocation="xsd/myOperation-v1.2.xsd"/>
    <xsd:import
    namespace=
    "<trimmed>/MyService-v1/yourOperation"
    schemaLocation="xsd/yourOperation-v1.2.xsd"/>
    </schema>
    </types>

    <!-- messages omitted -->

    <portType name="MyServicePort-v1.2">
    <operation name="myOperation">
    <!-- input, output and fault omitted -->
    </operation>
    <operation name="yourOperation">
    <!-- input, output and fault omitted -->
    </operation>
    </portType>

    <!-- binding omitted -->

    <!-- service omitted -->

    </definitions>
    Dilema zní: pokud dojde ke změně kontraktu pouze v rámci jedné operace, změním verzi pouze u XSD, které této operaci odpovídá, nebo změním verzi u všech XSD, které jsou součástí kontraktu? (V obou případech samozřejmě verzujeme WSDL tak, jak bylo uvedeno v předešlém článku.)

    Pokud by se nám změnila operace myOperation, tak v prvním případě by import vypadal takto:
    <types>
    <xsd:schema>
    <xsd:import
    namespace=
    "<shortened>/MyService-v1/myOperation"
    schemaLocation="xsd/myOperation-v1.3.xsd"/>
    <xsd:import
    namespace=
    "<shortened>/MyService-v1/anotherOperation"
    schemaLocation="xsd/anotherOperation-v1.2.xsd"/>
    </schema>
    </types>

    <portType name="MyServicePort-v1.3">
    <!-- rest omitted -->
    Výhodou této volby je, že verzujeme pouze XSD, u nichž opravdu došlo ke změně, zbytek necháváme na pokoji. Nevýhodou je, že verze XSD se nám rozjedou - jednak vůči sobě a jednak vůči WSDL. Reálně pak verzujeme XSD jako samostatný artefakt a ne jako součást kontraktu. To může být korektní situace, někdy vznikají XSD bez vazby na WSDL - v rozsáhlých projektech jsou často XSD vytvářena týmy analytiků a jejich import do WSDL zajišťují vývojáři.

    V případě druhé možnosti by import vypadal následovně:
    <types>
    <xsd:schema>
    <xsd:import
    namespace=
    "<shortened>/MyService-v1/myOperation"
    schemaLocation="xsd/myOperation-v1.3.xsd"/>
    <xsd:import
    namespace=
    "<shortened>/MyService-v1/anotherOperation"
    schemaLocation="xsd/anotherOperation-v1.3.xsd"/>
    </schema>
    </types>

    <portType name="MyServicePort-v1.3">
    <!-- rest omitted -->
    Nevýhodou této možnosti je, že musíme převerzovat všechy XSD v kontraktu, i ty, které se nezměnily (což se může zdát nepřirozené). Výhodou je, že kontrakt má jednotné verzování, takže mmj. nemusím kontrolovat jestli mám všechny XSD ve správné verzi. Zároveň je explicitně řečeno, že XSD je součástí kontraktu (ve smyslu objektové kompozice) a kontrakt (WSDL + XSD) by měl být vytvářen jedním týmem.

    U nás na projektu o tom byla poměrně bouřlivá diskuze, kterou variantu zvolit. Argumenty byly poměrně vyrovnané a více méně odpovídaly těm uvedeným výše. Nakonec jsme se rozhodli pro jednotný kontrakt, takže se změnou verze WSDL převerzováváme i všechny XSD.

    Verzování webových služeb, SOAP

    ·4 min
    Před časem jsem napsal lehký úvod do SOA governance. Chtěl bych se k tomuto tématu vracet a prezentovat principy, které postupně zavádíme na projektu. Momentálně nás daleko víc pálí věci, které spadají do design fáze služeb, takže řešíme věci jako verzování, reusabilita, granularita služeb apod.

    Aspekt, který jsme řešili jako první (a nyní už i zavedli v praxi) je verzování služeb. Jelikož používáme řešení založené na SOAP webových službách (jak jinak v enterprise :-) podíval bych se právě na toto téma. Jestliže budu dále mluvit o webových službách (WS), mám tím vždy na mysli WS založený na SOAP.

    Proč?

    Otázka je, proč vlastně webové služby verzovat? Dejme tomu, že z nějakého důvodu chceme, na určitém prostředí, držet více verzí jedné služby. Tím důvodem může být např. to, že už máme nějaké stávající konzumenty dané služby a zároveň chceme touto službou poskytnout nové (business) funkcionality. Stávající klienti ovšem nemohou (nebo také odmítnou) přejít na novou verzi služby. Starou verzi služby tedy necháme funkční a zároveň nasadíme verzi novou. V případě SOA by toto mělo být definováno/podpořeno nějakou politikou, např. podpora více verzí služeb.



    Kontrakt

    Jak z hlediska SOA, tak z hlediska klienta se webová služba (a její konzumace) točí kolem kontraktu. Kontrakt, jako takový, se skládá z několika částí. Jednak definuje nějaké designové věci jako datové typy, zprávy a rozhraní (ve smyslu OOP). Dále definuje technologické záležitosti, tj. jaké se používají protokoly, jaké jsou endpointy služby. A konečně jsou to nefunkční požadavky.

    Pro SOAPovské služby je tímto kontraktem WSDL (Web Service Definition Language). Pokud pomineme nefunkční požadavky (které stejně nejsou definovány ve WSDL namespacu, ale jinde), má WSDL následující strukturu (zeleně jsou designové části, červeně implementační):

    • types - element definující pomocí XML Schema datové typy používané ve zprávách.
    • message - abstraktní definice přenášených dat (zpráv), sestavená z typů definovaných v předešlém elementu. Každá zpráva se skládá z jedné a více logických částí (parts). Pokud je částí ve zprávě více, jde o službu založenou na RPC.
    • portType - množina abstraktních operací, víceméně odpovídá rozhraní v OOP. Každá operace (metoda) by měla odkazovat vstupní a výstupní zprávu. Operace může být jedním ze čtyř typů: Request-response (klasika), One-way (obsahuje pouze vstupní zprávu), Notification (obsahuje pouze výstupní zprávu) a Solicit-response (endpoint pošle zprávu a očekává odpověď).
    • binding - sváže definovaný portType s konkrétním protokolem (SOAP, HTTP, JMS) a formátem zpráv (např. Document/literal, RPC/encoded).
    • port - definuje endpoint služby.
    • service - množina souvisejících portů.

    Sémantika verzování

    Není potřeba znovu-vymýšlet-kolo. Archetypem verzování je formát <major>.<minor>.<micro>. U služeb nás micro verze nebude moc zajímat - je vyhrazená pro implementační změny, které neovlivňují vyšší řády verze a nijak se tedy nepromítají do kontraktu. minor verze je vyhrazená pro změny rozhraní, které jsou zpětně kompatibilní. No a major verze indikuje změnu rozhraní, která není zpětně kompatibilní, tj. rozbíjí kontrakt.

    Změny, které jsou zpětně kompatibilní:
    • přidání nové operace do služby,
    • přidání nového XML typu do schématu.

    Změny, které nejsou zpětně kompatibilní:
    • odebrání operace ze služby,
    • přejmenování operace,
    • změna XML typů a atributů zprávy,
    • změna namespace.

    Jak?

    Jako best-practice se uvádí, že verzování by pro konzumenty mělo být explicitní, tj. uvádět číslo verze v elementech, URL apod. Co všechno v kontraktu verzovat, může být předmětem diskuzí na konkrétním projektu a technologiích, nicméně dá se vyjít z těchnto pravidel:
    1. Vkládat major a minor verzi do názvu WSDL souboru: MyService-v1.2.wsdl.
    2. Vkládat major verzi do targetNamespace WSDL souboru:
      <definition
        targetNamespace=
            "http://sw-samuraj.blog/ws/MyService-v1"
        xmlns="http://schemas.xmlsoap.org/wsdl/">
    3. Vkládat major a minor verzi do portType elementu:
      <portType name="MyServicePort-v1.2">
    4. Vkládat major a minor verzi do service elementu:
      <service name="MyService-v1.2">
    5. Vkládat major verzi do endpointu služby:
      <soap:address
        location=
          "http://sw-samuraj.blog/myService/v1"/>
    Pokud bychom si ukázali celé WSDL, vypadalo by takto:
    <?xml version="1.0" encoding="UTF-8"?>
    <definitions
    targetNamespace=
    "http://sw-samuraj.blog/ws/MyService-v1"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <types>
    <xsd:schema
    targetNamespace=
    "http://sw-samuraj.blog/ws/MyService-v1">
    <!-- schema omitted -->
    </xsd:schema>
    </types>

    <!-- messages omitted -->

    <portType name="MyServicePort-v1.2">
    <!-- operations omitted -->
    </portType>

    <!-- binding omitted -->

    <service name="MyService-v1.2">
    <port binding="MyServiceSOAP"
    name="MyServiceSOAP">
    <soap:address
    location="http://sw-samuraj.blog/myService/v1"/>
    </port>
    </service>

    </definitions>

    Závěr

    Verzování poměrně úzce souvisí s dalším SOA governance aspektem a sice životním cyklem služeb (což je téma, na které se podíváme někdy příště). To že držíme na prostředí (typicky produkci) více verzí jedné služby má samozřejmě svoje náklady a ideálním stavem je, když služba běží pouze v jedné (nejaktuálnější) verzi.

    Se správou více verzí nám může pomoci vhodný nástroj - SOA governance repository, která drží o konrétní verzi služby různá meta-data a mmj. také to, v jaké životní fázi se služba nachází, nebo kdo ji konzumuje (a koho musíme notifikovat o penzionování služby).

    Management za zavřenými dveřmi

    ·4 min
    Když mám problém, sáhnu po knize. Samozřejmě, chytrá kniha sama o sobě nestačí. Ale pro mne je to dobrý odrazový bod, jak se zorientovat v neznámých vodách. Plavat už člověk musí sám.

    Když se řeší technologické věci, bývá to většinou jednoduché, často zábavné, někdy frustrující. Když se řeší problémy s lidmi, bývá to většinou složité, často frustrující a hlavně - nejednoznačné.

    Problém podobný tomu aktuálnímu jsem řešil cca před rokem - přišel jsem jako team leader do týmu vývojářů. Tým nebyl zrovna vstřícně naladěn, někteří členové byli až nepřátelští. Protože jsem podobnou situaci zatím nikdy neřešil, koupil jsem si tematickou knihu (viz post (Ne)funkční tým) a zkusil jsem se s tím (z dnešního pohledu bych řekl, že neúspěšně) poprat.

    Nevstoupíš dvakrát do téže řeky. Takže po roce: stejná role (teamleader), jiný tým (nesrovnatelně pozitivněji naladěn), podobný projekt (integrace na middlewaru) a úplně jiný problém. To si vám takhle týmlídruju, v týmu mám pět lidí, 50 % času věnuju řízení (takový ty JIRY, statusy a tak), 50 % věnuju vývoji. Radost pracovat. (Pozor, teď přijde zápletka.) Najednou: BUM, PRÁSK! Tým se ztrojnásobí! Nekecám, 16 lidí?!? Navíc, byť to téměř všichni byli seniorní javisti, z hlediska použité technologie a architektury (SOA), byli v podstatě juniorní a náběhová a učící křivka byla velmi dlouhá (2 měsíce).

    Šestnáct lidí je opravdu hodně. Maximum, co mám teoreticky a empiricky ověřeno, je deset a člověku to zabere 100 % času. Na nějaké kódování může zcela zapomenout. Šestnáct lidí, to už není teamleading, to už je čistý management. Ehm. Já, excelentní vývojář ;-) a management? Jak říkám: když mám problém, sáhnu po knize.

    Vybral jsem si titul Behind Closed Door, jejíž podtitul slibuje odhalit Secrets of Great Management. Nevím, jestli se dodržováním principů uvedených v knize, stane člověk skvělým manažerem. Ale to ani nebyl můj cíl. Chtěl jsem si jen ověřit, že některé věci dělám správně, že v dobré víře nedělám chyby a taky si trochu rozšířit repertoár.

    Ostřílení matadoři by asi nad radami v knize mávli rukou, že to není nic nového, je to samozřejmost atd. Ano, ony to opravdu jsou jednoduché věci. Ale to asi nejtěžší na tom je, být konzistentní a perzistentní :-)

    Kniha používá osvědčený formát - fiktivní příběh fiktivního manažera (který přijde nově do společnosti, vyřeší všechny problémy a všichni jsou šťastný a firma vydělá balík), prokládaný konkrétními tipy a postupy jak danou věc dělat/řešit. V průběhu příběhu jsou postupně probírána témata jako: delegování, prioritizace, zpětná vazba, koučování, one-on-one pohovory, prosazování vlivu apod.

    Pro mne byly z knihy nejpřínosnější dvě témata. Jednak jsem se utvrdil v tom, že je důležité dělat one-on-one pohovory a držet je pravidelně. Už nějaký čas je dělám ve všech týmech a myslím, že to je jedna z věcí, která velmi podstatně zvedne důvěru v týmu. A důvěra to je základ, na kterém je dobrý tým postaven.

    Druhým podstatným tématem pro mne byla zpětná vazba a přesněji korektivní zpětná vazba. Možná to znáte - lidi v týmu pracují většinou dobře, ale jeden/dva z nich podávají slabší výkon. Je to delikátní situace, vždycky je tu nějaký důvod, proč tomu tak je. Pokud má člověk zájem (a je mu to důstojně podáno), často se dají překážky, které brání kvalitní práci odstranit. V knize je popsáno, na co si dát pozor, aby se komunikace nepřeklopila do iracionální roviny. A taky, kdy to už nemá smysl řešit.

    Komu bych knihu doporučil? Určitě začínajícím teamleaderům. Byť není zaměřena na technické vedení lidí a projektů se dotýká jen zlehka, je to dobrý začátek, protože jako teamleader budete komunikovat primárně se svým týmem a sekundárně s projektem a jinými týmy. Doporučil bych ji i pokud máte, tak jako já, nějakou tu teamleaderovskou zkušenost za sebou. Vždycky je totiž něco nového, co se dá naučit a stávající zlepšit. Třetím doporučením je pak edice, v které  knížka vyšla - vydavatelství The Pragmatic Programmers je zárokou kvalitní softwarová literatury a mne nezklamala ani tentokrát.

    No a jak se vyvinul můj v úvodu zmíněný problém? Tým se mezitím stabilizoval a taky se smrsknul na rozumnou úroveň deseti lidí. Držím pravidelné one-on-one pohovory jednou za měsíc. V jednom případě jsem si vyzkoušel korektivní zpětnou vazbu s následným dohledem, což nakonec, bohužel, vyústilo v odchod daného člověka z týmu. Taky jsem, s těžkým srdcem, musel delegovat několik Proof of Concept, na který jsem se moc těšil. Tak snad to aspoň pomohlo někomu jinému. Je toho samozřejmě daleko víc, co dělám (a co je potřeba dělat), aby jsme měli dobrý tým. Uvedl jsem pouze věci, které přímo souvisí a jsou tématy představené knihy.

    Certifikace Java EE 6 Web Services Developer

    Tak nějak jsem si poslední dobou zvykl, dělat do roka dvě certifikace (asi mi chybí školní dril :-) Takže co to bylo tentokrát? Honosný název, který se nyní skví v mém CV zní: Oracle Certified Expert, Java Platform, Enterprise Edition 6 Web Services Developer, zkráceně (ve stylu někdejších Sunovských certifikací) OCWSD.

    Certifikace občas budí (zbytečné) vášně, takže základní otázka asi je: je to vůbec k něčemu? Pro mne je tím největším benefitem, že jsem se něco nového (do hloubky) naučil. A musím říct, že OSWSD je tomto směru opravdu hodnotná. Byť web servisy mastím cca poslední tři roky, ať už v Javě (JAX-WS, Spring WS nebo Axis), nebo v rámci SOA (WebSphere Message Broker, Oracle SOA Suite), tak během přípravy na certifikaci jsem dozvěděl spoustu nových věcí (třeba SOAP Message Handlers) a "dopochopil" lecos již známého.

    Témata, která zkouška obsahovala jsou následující:
    • Create an SOAP web service in a servlet container
    • Create a RESTful web service in a servlet container
    • Create a SOAP based web service implemented by an EJB component
    • Create a RESTful web service implemented by an EJB component
    • Configure JavaEE security for a SOAP web service
    • Create a web service client for a SOAP based web service
    • Create a web service client for a RESTful web service
    • Create a SOAP based web service using Java SE platform
    • Create handlers for SOAP web services
    • Create low-level SOAP web services
    • Use MTOM and MIME in a SOAP web service
    • Use WS-Addressing with a SOAP web service
    • Configure Message Level security for a SOAP web service
    • Apply best practices to design and implement web services

    Protože na zkoušku není žádný oficiální certifikační guide, prošel jsem trošku širší okruh materiálů. V praxi jsem zatím neměl nikdy možnost dostat se k RESTovým technologiím, takže jsem s předstihem začal s knížkou Billa Burkeho RESTful Java with JAX-RS, kterou vřele doporučuji - pokud chcete začít s RESTem v Javě (tj. libovolnou implementací JAX-RS), je to ta správná volba. Autor sice popisuje (a je tvůrcem) RESTEasy, ale je objektivní i vůči dalším implementacím.

    Další knihou jsem si chtěl oživit druhou Javovskou web servisovou větev - JAX-WS. Tady je knižní situace docela slabá. V podstatě jediná aktuální kniha, která je věnována JAX-WS je Java 7 JAX-WS Web Services. Vzhledem k názvu a rozsahu (64 str.) jsem nečekal nic světoborného, ale zklamání bylo obrovské. Pokud nejste úplně nejzelenější začátečník a nevyhovují vám knížky, tvořené ze 2/3 screenshoty (NetBeansů), není to kniha pro vás. Škoda peněz, škoda času.

    Jakou určitý druh přípravy můžu zmínit (celkem nudný) letošní Oracle Java Developer Day, kde jsem se šel podívat na jednu přednášku o JAX-WS a na dvě o JAX-RS. Samozřejmě, že to byly naprosté základy, ale člověk si aspoň udržuje povědomí o tématu.

    Pak už začalo jít do tuhého, protože jsem potřeboval něco, co by mě připravilo na certifikaci. Nechal jsem tedy firmu zakoupit zkušební testy. Tentokrát jsem zvolil ještě nevyzkoušené řešení - kit od EPractize Labs. Jejich OCEJWSD Training Lab není úplně špatný. Sice je uživatelsky dost nepohodlný a rozložení témat mi přijde dost nevyvážené, ale myslím, že ho můžu označit jako přínosný. V dnešní době bych spíš volil kit od uCertify, s jejichž přípravnými testy mám dobrou zkušenost, který ale v té době ještě nebyl k dispozici.

    Posledním materiálem, který jsem na přípravu použil je OCWSD Study Guide od Mikalaie Zaikina, který pokrývá témata zkoušky velmi pěkně. Linkovaná webová verze je zdarma, za $15 je možné si přikoupit sadu 160 otázek s odpověďmi, plus jednotlivé guidy v PDF podobě. Těch patnáct dolarů jsem investoval a můžu říct, že to stálo za to (aneb jak jsme se učili v angličtině it bang for the buck).

    Pokud mám certifikaci OCWSD nějak celkově zhodnotit, musím říct, že je to určitě jedna z těch nejpřínosnějších, co dnes Oracle v oblasti Javy nabízí. Všechna témata byla víceméně ryze praktická, žádná nudná (korporátní) teorie, či dokonce ideologie. Zároveň je to taky jedna z těch, kde je těch Oraclovských technologií jen určité minimum - hodně se tam probírají věci z W3C, jako je SOAP, WSDL, XSD, WS-Addressing, nebo věci, které jsou dílem různých jiných organizací, jsou je WS-I (WS-Interoperability (Basic Profile)), nebo WS-Security.