Přeskočit na hlavní obsah
  1. Tags/

Soap

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

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).

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.