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

<?xml version="1.0" encoding="utf-8"?>
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP:Body>
    <withdraw>
      <amount>1200.0</amount>
    </withdraw>
  </SOAP:Body>
</SOAP:Envelope>

\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}
<customer>
  <firstName>Michal</firstName>
  <surname>Novotný</surname>
</customer>
\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}
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <S:Body>
\codeHighlight{    <ns2:getCustomerResponse xmlns:ns2="piggybank">}
\codeHighlight{      <customer>}
\codeHighlight{        <age>178</age>}
\codeHighlight{        <name>Jan_2</name>}
\codeHighlight{      </customer>}
\codeHighlight{    </ns2:registerResponse>}
  </S:Body>
</S:Envelope>
\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}\\
