%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% :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}\\