%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% :Axis %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Axis} Axis je alternativní framework k~JAX-WS od Apache. Umožňuje vytvořit jak SOAP webovou službu, tak klienta pro takovouto službu. Jelikož Axis je založen čistě na knihovnách, je možné s~ním vytvořit i~desktopového klienta s~uživatelským rozhraním například ve Swingu, který se bude umět k~webové službě připojit. V~této kapitole si popíšeme poslední verzi Axisu -- verzi 1.4. \subsection{Vytvoření webové služby} Vytvoření webové služby v~Axisu je snadné. Předpokládejme, že chceme vytvořit aplikaci Piggy Bank a~že máme třídu nazvanou \texttt{Account} která bude mít prozatím definovánu jednu metodu -- \texttt{withdraw}. Tuto třídu umístníme do \emph{implicitního bezejmenného balíčku}. Ušetříme si tak zbytečné komplikace s~adresou později vytvořeného koncového bodu. Zdrojový kód této třídy bude vypadat následovně: \begin{Verbatim} public class Account \{ private double balance; public double withdraw(double amount) throws WithdrawException \{ if (amount > balance) \{ throw new WithdrawException("Unable to withdraw " + "more than current balance."); \} balance -= amount; return balance; \} \} \end{Verbatim} \subsection{Nasazení} Nejprve potřebujeme nakonfigurovat zpracovávající servlet. Ten je v~našem případě \texttt{AxisServlet} z~balíčku \texttt{org.apache.axis.transport.http}. Předpokládejme, že pro něj namapujeme URL \emph{/axis}. Teď musíme vytvořit v~kořenovém adresáři webové aplikace podadresář \emph{axis} (je to stejné jméno, jako namapovaná URL). Dále musíme vytvořit kopii souboru námi vytvořené třídy \texttt{Account}, pojmenovat ji \emph{Account.jws} a~vložit do adresáře \emph{axis}. Celá cesta je tedy \emph{/axis/Account.jws}. Tím zpřístupníme tuto třídu jako koncový bod na adrese \emph{www.piggybank.com/axis/Account.jws}. Servlet Axisu ji pak za chodu sám najde. Webové metody mají stejné jméno jako metody třídy. Požadavek na volání webové metody \texttt{withdraw} by pak mohl vypadat následovně: \begin{Verbatim} POST /axis/Account.jws HTTP/1.1 Host: www.piggybank.com SOAPAction: withdraw Content-Type: application/soap+xml Content-Length: 260 1200.0 \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Všimněte si, že na rozdíl od JAX-WS, Axis implicitně nepoužívá pro webové metody jmenné prostory. Také si všimněte pole hlavičky \texttt{SOAPAction}. To je bohužel servletem Axisu vyžadované, i~když jeho hodnotu servlet sám ignoruje. }} Komplexnější konfigurace se v~Axisu dá vyřešit pomocí souboru WSDD, který je popsán v~\cite{Axis User's Guide}. \subsection{Klient pro webovou službu} Na první pohled se může naprogramování klienta v~Axisu zdát trochu složité a~neohrabané. Je však třeba brát v~potaz, že Axis pracuje výhradně se svými knihovnami. Odpadají reference na vzdálený WSDL soubor stejně tak jako autogenerace tříd a~rozhraní. Jak již bylo zmíněno, může aplikace využívající Axis běžet na uživatelském počítači. \subsection{Vytvoření požadavku} Každý požadavek, který budeme zasílat na server s~webovou službou musí být objekt typu \texttt{Call} z~balíčku \texttt{org.apache.axis.client}. Takovýto objekt vytvoříme konstruktorem, který bere jeden parametr -- URL koncového bodu webové služby, na který chceme poslat svůj požadavek. Tuto URL pak můžeme zadat jako řetězec, nebo jako instanci třídy \texttt{URL} ze standardní knihovny Javy. \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); \end{Verbatim} \subsection{Nastavení jména operace} Jakmile máme objekt typu \texttt{Call}, můžeme nastavit jméno webové metody, kterou chceme volat. To se udělá jednoduše zavoláním metody \texttt{setOperationName} na objektu požadavku. Zde máme dvě možnosti. Pokud webová požadovaná metoda neleží uvnitř žádného prostoru jmen, můžeme zadat její jméno jako řetězec. \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); \codeHighlight{call.setOperationName("getBalance");} \end{Verbatim} Pokud leží uvnitř nějakého prostoru jmen, musíme použít objekt typu \texttt{QName} z~balíčku \texttt{javax.xml.namespace}. Ten vytvoříme pomocí konstruktoru se dvěma parametry -- prvním bude název jmenného prostoru, druhým název webové metody. \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); \codeHighlight{call.setOperationName(new QName("piggybank", "deposit"));} \end{Verbatim} \subsection{Nastavení stylu a~použití u~SOAP zprávy} Styl a~použití SOAP zprávy (podrobnosti v~kapitole \ref{SOAP Style and Use}) jsou v~Axisu implicitně nastaveny jako RPC\/ENCODED, zatímco v~současné době je zvykem používat zprávy DOCUMENT\/LITERAL. Pokud tedy i~vy chcete používat styl DOCUMENT a~použití LITERAL, musíte obě hodnoty změnit pomocí příslušných nastavovacích metod -- \texttt{setOperationStyle} a~\texttt{setOperationName}. Jak je už v~Axisu zvykem, obě metody mají přetížené verze. Parametr můžeme zadat jako položku z~výčtového typu \texttt{Style}, resp. \texttt{Use} (oba z~balíčku \texttt{org.apache.axis.constants}, nebo jako řetězcovou konstantu. Pozor, řetězcová konstanta musí být psaná celá malými písmeny, tedy např. "document". \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); call.setOperationName(new QName("piggybank", "deposit")); \codeHighlight{call.setOperationStyle(Style.DOCUMENT);} \codeHighlight{call.setOperationUse(Use.LITERAL);} \end{Verbatim} \subsection{Zadávání parametrů} Zadávání parametrů pro webovou metodu je v~Axisu bohužel dosti nešikovné. Každý parametr musíte nejdříve deklaravot voláním metody \texttt{addParameter} instance třídy \texttt{Call}. Tím se parametr přidá do fronty, platí tedy, že parametry musíme deklarovat ve stejném pořadí, v~jakém je webová metoda přijímá. Metoda \texttt{addParameter} bere sama 3 parametry -- název webového parametru, typ webového parametru (možné hodnoty typu viz. XML Schema), a~mód webového parametru. Název webového parametru je jednoduchý. Typ webového parametru musíme zadat jako položku výčtového typu \texttt{Constants} z~balíčku \texttt{org.apache.axis}. Jedná se o~typy shodné s~XML Schematem, tedy např. \texttt{XSD\_STRING} pro řetězec. Poslední parametr --- mód webového parametru je pro nás vždy položka \texttt{IN} z~výčtového typu \texttt{ParameterMode} balíčku \texttt{javax.xml.rpc}. Všimněte si, že jsme zatím nikde nezadali (ani jsme nemohli zadat) hodnoty webových parametrů. \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); call.setOperationName(new QName("piggybank", "deposit")); call.setOperationStyle(Style.DOCUMENT); call.setOperationUse(Use.LITERAL); \codeHighlight{call.addParameter("amount", Constants.XSD_DOUBLE, ParameterStyle.IN);} \end{Verbatim} Tímto jsme deklarovali parametr \texttt{amount} pro webovou metodu \texttt{deposit}. Typ parametru jsme nastavili jako \texttt{XSD\_DOUBLE} (odpovídá primitivnímu typu \texttt{double} z~jazyka Java) a~celý parametr jsme označili jako vstupní. Samotnou hodnotu parametru můžeme zadat až později. \subsection{Volání metody} Webovou metodu zavoláme pomocí metody \texttt{invoke} na instanci třídy \texttt{Call}. Metodě \texttt{invoke} zadáme webové parametry jako pole instancí typu \texttt{Object}. Toto pole představuje vektor parametrů, které jsme předtím deklarovali. Netřeba dodávat, že hodnoty těchto parametrů musí následovat ve stejném pořadí, v~jakém byly parametry nadeklarovány. V~případě, že námi volaná webová metoda nebere žádné parametry, musíme zadat pole parametrů jako prázdné pole (tedy např. \texttt{call.invoke(new Object[0]);}). \begin{Verbatim} Call call = new Call("http://www.piggybank.com/account"); call.setOperationName(new QName("piggybank", "deposit")); call.setOperationStyle(Style.DOCUMENT); call.setOperationUse(Use.LITERAL); call.addParameter("amount", Constants.XSD_DOUBLE, ParameterStyle.IN); \codeHighlight{call.invoke(new Object[] \{1000\});} \end{Verbatim} \subsection{Získávání objektů} Implicitně umí Axis jako návratovou hodnotu přečíst pouze základní datový typ jako jeden XML element. Většina webových služeb ale jako odpověď zasílá objekty. Dokonce i~při vracení desetinného čísla (v~případě příkladu Piggy Bank u~webové metody \texttt{getBalance}) framework JAX-WS obaluje navrácenou hodnotu do elementu \texttt{getBalanceResponse}. Z~hlediska Axisu se jedná o~objekt s~názvem \texttt{getBalanceResponse}, který obsahuje jeden atribut -- \texttt{balance}. Pro každý objekt, který očekáváme, musíme mít vytvořenou třídu typu JavaBean. Ve zmiňovaném případě by se mohla jmenovat \texttt{BalanceResponse}. Ta bude mít jeden atribut typu \texttt{double} pojmenovaný \texttt{balance}. Tuto třídu pak musíme v~Axisu zaregistrovat pomocí metody \texttt{registerTypeMapping} instance třídy \texttt{Call}. \begin{Verbatim} public class BalanceResponse \{ private double balance; public double getBalance() \{ return balance; \} public void setBalance(double balance) \{ this.balance = balance; \} \} \codeHighlight{call.registerTypeMapping(BalanceType.class,} \codeHighlight{ new QName("piggybank", "getBalanceResponse"),} \codeHighlight{ BeanSerializerFactory.class,} \codeHighlight{ BeanDeserializerFactory.class);} \end{Verbatim} Všimněte si parametrů metody \texttt{registerTypeMapping}. Prvním z~nich je třída, kterou chceme registrovat, tedy \texttt{BalanceResponse}. Druhým je XML element ze SOAP odpovědi, který se bude mapovat na tuto třídu. V~našem případě je to element \texttt{getBalanceResponse} (dle dokumentace webové služby) ležící v~XML prostoru jmen \texttt{piggybank}. Poslední dva parametry jsou serializační a~deserializační tovární třídy. Jelikož je námi navržený objekt typu JavaBean, můžeme využít předdefinované továrny \texttt{BeanSerializerFactory} a~\texttt{BeanDeserializerFactory}, obě z~balíčku\texttt{org.apache.axis.encoding.ser}. Zde vidíte jednu z~výhod JavaBeanů -- pro práci s~nimi často existují již vytvořené třídy. Nyní předpokládejme, že v~SOAP odpovědi webové služby obdržíme skutečný objekt. Vezměme si třeba objekt \texttt{customer} popsaný v~XML takto: \begin{Verbatim} Michal Novotný \end{Verbatim} Předpokládejme, že webová služba takovýto objekt zabalí do XML elementu \texttt{getCustomerResponse} stejně jako Piggy Bank vytvořený v~JAX-WS obaluje výsledek metody \texttt{getBalance}. Celá SOAP odpověď tedy může vypadat například takto: \begin{Verbatim} \codeHighlight{ } \codeHighlight{ } \codeHighlight{ 178} \codeHighlight{ Jan_2} \codeHighlight{ } \codeHighlight{ } \end{Verbatim} Pro takovýto objekt musíme mít vytvořené dva JavaBeany -- jeden pro XML element \texttt{getCustomerResponse}, druhý pro \texttt{customer}. Oba JavaBeany by mohly vypadat takto: \begin{Verbatim} public class Customer \{ private String firstName; private String surname; public String getFirstName() \{ return firstName; \} public void setFirstName(String firstName) \{ this.firstName = firstName; \} public String getSurname() \{ return surname; \} public void setSurname(String surname) \{ this.surname = surname; \} \} public class CustomerResponse \{ private Customer customer; public Customer getCustomer() \{ return customer; \} public void setCustomer(Customer customer) \{ this.customer = customer; \} \} \end{Verbatim} Poté, co máme jejich třídy navržené, je potřeba obě dvě zaregistrovat. Postup je stejný, jako u~registrování jedné třídy, ukážeme si tedy jen výsledný kód. \begin{Verbatim} call.registerTypeMapping(Customer.class, new QName("customer"), BeanSerializerFactory.class, BeanDeserializerFactory.class); call.registerTypeMapping(CustomerResponse.class, new QName("piggybank", "getCustomerResponse"), BeanSerializerFactory.class, BeanDeserializerFactory.class); \end{Verbatim} Tímto způsobem ví Axis vše potřebné ke správnému sestavení odpovědi jako instance třídy \texttt{CustomerResponse} v~jazyce Java. Interní XML parser Xerces (typ SAX) pak výsledný objekt sestaví způsobem, jaký byl popsán v~kapitole \ref{SAX} \subsection{Zpracování výjimek} Zpracování výjimky u~webové služby je v~Axisu snadné. U~metody \texttt{invoke} musíme povinně zachytit možnou výjimku v~podobě instance třídy \texttt{AxisFault} z~balíčku \texttt{org.apache.axis}. Tato instance v~sobě obsahuje všechny informace ze SOAP výjimky. \begin{Verbatim} \codeHighlight{try \{} Call call = new Call("http://www.piggybank.com/account"); call.setOperationName(new QName("piggybank", "deposit")); call.setOperationStyle(Style.DOCUMENT); call.setOperationUse(Use.LITERAL); call.addParameter("amount", Constants.XSD_DOUBLE, ParameterStyle.IN); call.invoke(new Object[] \{1000\}); \codeHighlight{\} } \codeHighlight{catch (AxisFault ex) \{} \codeHighlight{ System.err.println(ex.getMessage());} \codeHighlight{ ex.printStackTrace();} \codeHighlight{\}} \end{Verbatim} \subsection{Sporné body} Stejně jako mnohé jiné projekty od Apache, i~Axis prošel mezi svými verzemi řadou divokých změn. Změny mezi verzí popsanou v~\cite{Web Services Essentials} (toho času s~názvem Apache SOAP) a~verzí 1.4 popsanou zde jsou natolik velké, že zdroje a~tutoriály staršího data dnes již prakticky není možné použít. Změnily se názvy metod, třídy leží v~jiných balíčcích, změnil se i~celkový návrh volání webové metody. Vše komplikuje i~fakt, že metodám a~objektům často chybí dokumentace, uživatel je pak odkázán na studování aktuálních online příkladů. \subsection{Závěr} Axis poskytuje kvalitní nástroj jak pro vytvoření webové služby, tak pro vytvoření klientské aplikace. Přestože modernější framework JAX-WS poskytuje propracovanější návrh a~nabízí snazší implementaci služby včetně nasazení, udržuje si Axis stále svou oblibu. Klientský kód v~Axisu obsahuje některé nešťastné návrhy, obzvláště zadávání webových parametrů. Je to zapříčiněno tím, že Axis je stavěn na staré technologii JAX-RPC (její pozdější vylepšená verze byla přejmenována právě na JAX-WS) a~musí implementovat její, z~dnešního pohledu špatně navržená, rozhraní. I~přes všechny zmíněné nedostatky je Axis použitelný a~stále používaný. Použitá literatura:\\ \cite{Axis User's Guide} \emph{Uživatelská příručka Axisu}\\ \cite{Axis API} \emph{Dokumentace Axisu}\\