<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<article>
  <title>Apache Axis - tutoriál k webovým službám</title>

  <section>
    <title>Úvod</title>

    <para></para>
  </section>

  <section>
    <title>Príprava projektu</title>

    <para>Axis vyžaduje na svoje správne fungovanie webovú aplikáciu, ktorú je
    možno nasadiť v servletovom kontajneri. Na to je potrebné mať pripravenú
    adresárovú štruktúru zodpovedajúcu špecifikácii pre webové
    aplikácie.</para>

    <para>V obľúbenom vývojovom prostredí teda vytvoríme nový projekt.</para>

    <para>Do adresára <filename>WEB-INF/classes</filename> je nutné ukladať
    skompilované triedy. Do adresára knižníc <filename>WEB-INF/lib</filename>
    potrebujeme skopírovať všetky JAR knižnice z distribúcie Axisu.</para>

    <para>Upravíme popisovač nasadenia webovej aplikácie, t. j. súbor
    <filename>WEB-INF/web.xml</filename>. Doň pridáme definíciu axisovského
    servletu spolu s jeho mapovaním.</para>

    <programlisting>&lt;servlet&gt;
  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
  &lt;display-name&gt;Apache-Axis Servlet&lt;/display-name&gt;
  &lt;servlet-class&gt;
    org.apache.axis.transport.http.AxisServlet
  &lt;/servlet-class&gt;
&lt;/servlet&gt;

&lt;servlet-mapping&gt;
  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/services/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;  </programlisting>

    <para>Ak mienime používať aj instantné webové služby (JWS), musíme pridať
    mapovanie aj pre ne.</para>

    <programlisting>&lt;servlet-mapping&gt;
  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
  &lt;url-pattern&gt;*.jws&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</programlisting>

    <para>Správnu funkcionalitu AxisServletu overíme navštívením príslušnej
    adresy. Ak sa naša aplikácia volá wstest a beží v kontajneri na porte
    8080, navštívime adresu <ulink url="???"><ulink
    url="http://localhost:8080/wstest/services">http://localhost:8080/wstest/services</ulink></ulink></para>
  </section>

  <section>
    <title>Konštrukcia instantnej webovej služby</title>

    <para>Najjednoduchší (quick-and-dirty) spôsob sprevádzkovanie webovej
    služby je vytvorenie instantnej triedy JWS. JWS je nič iné, než jednoduchá
    Java fazuľa uložená v súbore s príponou jws.</para>

    <para>Vytvoríme teda súbor <filename>InstantWeatherService.jws</filename>
    a umiestnime ho do koreňa webovej aplikácie (teda do adresára nadradeného
    adresáru <filename>WEB-INF</filename>).</para>

    <programlisting>public class InstantWeatherService {
  public String getCurrentWeather(String cityName) {
    return "V " + cityName + " je práve veľká zima.";
  }

  public float getCurrentTemperature(String cityName) {
    return 3.14f;
  }  

  public float[] getTemperaturePrediction(String cityName, int howManyDays) {
    float[] temperatures = new float[howManyDays];
    for(int i = 0; i &lt; howManyDays; i++) {
      temperatures[i] = new Double((Math.random() * 45) - 10).floatValue();
    }
    return temperatures;
  }  
}</programlisting>

    <para>Trieda nesmie mať vo svojom zdrojovom kóde uvedenú deklaráciu
    <token>package</token>, inak ju Axis nenájde.</para>

    <para>Otestovanie základnej funkčnosti je jednoduché. Browserom zamierime
    na adresu <ulink
    url="http://localhost:8080/wstest/InstantWeatherService.jws">http://localhost:8080/wstest/InstantWeatherService.jws</ulink>.
    Axis skompiluje túto triedu a jej metódy dá k dispozícii ako webovú
    službu. Ak všetko funguje správne, v browseri sa zobrazí jednoduchá
    stránka s uvítaním. Z nej zároveň vedie odkaz na WSDL našej čerstvej
    webovej služby.</para>

    <figure>
      <title>Uvítanie fungujúcej instantnej JWS webovej služby</title>

      <mediaobject>
        <imageobject>
          <imagedata fileref="instantservice.gif" />
        </imageobject>
      </mediaobject>
    </figure>
  </section>

  <section>
    <title>Konštrukcia klienta</title>

    <para>Výhodou Axisu je možnosť vygenerovať klientské triedy pre prístup k
    webservicu a to len na základe WSDL. Predpokladajme, že k nášmu
    primitívnemu WebServiceu chceme vygenerovať java triedy.</para>

    <para>To môžeme urobiť pomocou nástroja WSDL2Java dodávaného s Axisom. Ak
    predpokladáme, že v premennej prostredia AXICLASSPATH sú uvedené všetky
    knižnice JAR z distribúcie Axisu, tak príslušný príkaz vo Windowse je
    nasledovný.</para>

    <programlisting>java -cp %AXISCLASSPATH% org.apache.axis.wsdl.WSDL2Java --output . http://localhost:8080/wstest/PrimitiveWebService.jws?wsdl</programlisting>

    <para>Parameter <parameter>--output</parameter> umožní špecifikovať
    cieľový adresár, do ktorého sa vygenerujú klientské triedy.
    Najdôležitejšou je ale adresa (prípadne cesta v súborovom systéme), kde sa
    nachádza dokument WSDL.</para>

    <para>Po spustení príkazu sa nám v cieľovom adresári vytvorí niekoľko Java
    súborov (minimálne 4):</para>

    <para><itemizedlist>
        <listitem>
          <para><filename>localhost/wstest/PrimitiveWebService_jws/PrimitiveWebService.java</filename>
          je java interface obsahujúci práve tie metódy, ktoré webservice
          poskytuje. Pre každú vzdialenú procedúru, či funkciu webservicu je v
          tomto súbore uvedená príslušná java metóda. Samotný interface dedí
          od interfacu <classname>java.rmi.Remote</classname> a každá z metód
          vie hádzať výnimku
          <classname>java.rmi.RemoteException</classname>.</para>
        </listitem>

        <listitem>
          <para>localhost/wstest/PrimitiveWebService_jws/PrimitiveWebServiceService.java
          zodpovedá elementu <token>service.</token></para>
        </listitem>

        <listitem>
          <para>localhost/wstest/PrimitiveWebService_jws/PrimitiveWebServiceServiceLocator.java
          je v podstate továreň, ktorá vracia inštanciu triedy predstavujúcej
          WebService.</para>
        </listitem>

        <listitem>
          <para>localhost/wstest/PrimitiveWebService_jws/PrimitiveWebServiceSoapBindingStub.java
          je z hľadiska klienta najdôležitejšia trieda. V nej sa vykonáva
          špinavá práca - generujú sa v nej kódy klientského volania, definujú
          mapovania java objektov na XML a podobne.</para>
        </listitem>
      </itemizedlist></para>

    <section>
      <title>Konštrukcia testovacej triedy</title>

      <para>Teraz nám už môžeme poľahky vytvoriť jednoduchú testovaciu triedu
      na prístup k webserviceu.</para>

      <programlisting>import localhost.wstest.PrimitiveWebService_jws.PrimitiveWebService;
import localhost.wstest.PrimitiveWebService_jws.PrimitiveWebServiceServiceLocator;

public class PrimitiveWebServiceTest {

  public static void main(String[] args) throws Exception {
    PrimitiveWebServiceServiceLocator locator = new PrimitiveWebServiceServiceLocator();
    PrimitiveWebService ws = locator.getPrimitiveWebService();
    System.out.println(ws.echo("Ako sa do hory vola, tak sa ma z hory ozyvat"));
    System.out.println(ws.add(12, 13));
    System.out.println(ws.getCurrentDateTime());
    
  }
}</programlisting>

      <para>Po spustení triedy by nám program mal vypísať to, čo nám vrátila
      webová služba.</para>
    </section>
  </section>

  <section>
    <title>Konštrukcia štandardnej webovej služby</title>

    <para>Na konštrukciu webovej služby sa používajú principiálne dva
    postupy:</para>

    <itemizedlist>
      <listitem>
        <para>top-down: zhora nadol. Najprv sa manuálne pripraví WSDL a na
        jeho základe sa vytvoria serverovské i klientské triedy</para>
      </listitem>

      <listitem>
        <para>bottom-up: zdola nadol. Existujúce triedy sa prispôsobia na
        použitie v úlohe webovej služby.</para>
      </listitem>
    </itemizedlist>

    <para>Najprv sa budeme venovať spôsobu zdola nahor. Vytvoríme najprv
    triedy pre serverovskú stranu, z ktorých vygenerujeme WSDL a na základe
    tohto dokumentu vygenerujeme klientské triedy.</para>

    <section>
      <title>Metóda bottom-up</title>

      <para>Predpokladajme, že chceme vytvoriť webovú službu, ktorá bude
      reprezentovať meteorologický servis. Základným dátovým typom bude trieda
      <classname>Weather</classname></para>

      <programlisting>package axis.weather;

import java.util.Date;

public class Weather {
  private Date date;
  
  private float windSpeed;
  
  private float temperature;
  
  private String textStatus;

  /* nasleduju gettery a settery */
}  </programlisting>

      <para>Tento objekt bude využívať interface
      <classname>WeatherService</classname> poskytujúci predpoveď
      počasia.</para>

      <programlisting>package axis.weather;

import java.util.Date;

public interface WeatherService {
  /**
   * Vráti stav počasia na dnešný deň.
   * 
   */
  public Weather getWeatherForToday();

  /**
   * Vráti stav počasia zodpovedajúci príslušnému dátumu.  
   */
  public Weather getWeather(Date date);
  
  /**
   * Vráti pole stavov počasia na požadovaný počet dní dopredu.
   * 
   */
  public Weather[] getWeatherPrediction(int daysCount);
  
  /**
   * Vráti textové informácie o tejto službe
   * 
   */
  public String getServiceInfo();
}</programlisting>

      <para>Vo fáze nasadenia webovej služby musíme prirodzene vytvoriť
      implementáciu tohto interfaceu, budeme ju označovať
      <classname>WeatherServiceImpl.</classname></para>

      <para><programlisting>package axis.weather;

import java.util.Date;

public class WeatherServiceImpl implements WeatherService {

  public Weather getWeatherForToday() {
    return getRandomWeather(new Date());
  }
  
  public Weather getWeather(Date date) {
    return getRandomWeather(date);
  }

  public Weather[] getWeatherPrediction(int daysCount) {
    Weather[] weathers = new Weather[daysCount];
    for (int i = 0; i &lt; daysCount; i++) {
      Date d = new Date();
      d.setDate(d.getDay() + i);
      weathers[i] = getRandomWeather(d);
    }
    return weathers;
  }

  public String getServiceInfo() {
    return "This is WeatherService.";
  }

  private Weather getRandomWeather(Date d) {
    Weather w = new Weather();
    w.setDate(d);
    w.setTemperature(new Double((Math.random() * 45) - 10).floatValue());
    w.setTextStatus("Hmlisto.");
    w.setWindSpeed(new Double((Math.random() * 120) - 0).floatValue());
    
    return w;
  }
}</programlisting></para>

      <para>Takúto hotovú triedu môžeme pomerne priamočiaro nasadiť do Axisu
      ako webovú službu. Predtým však potrebujeme špecifikovať a doriešiť
      niekoľko konfiguračných záležitostí.</para>

      <section>
        <title>Popisovač nasadenia webovej služby a jej odstránenia zo
        serveru.</title>

        <para>V slušnej spoločnosti Axisovej je to tak, že každej množine
        webových služieb, ktoré môžu byť jednotne nasadené na serverovskú
        stranu prislúchajú dva súbory: <filename>deploy.wsdd</filename> a
        <filename>undeploy.wsdd</filename>.</para>

        <itemizedlist>
          <listitem>
            <para>deploy.wsdd - špecifikuje parametre webovej služby: jej
            názov; triedu, ktorá ju implementuje, prípadný zoznam metód a
            názov portu a cieľového menného priestoru vo WSDL.</para>

            <para>Tento súbor zároveň obsahuje zoznam spôsobov mapovaní
            vlastných dátových typov na XML.</para>
          </listitem>

          <listitem>
            <para>undeploy.wsdd - jednoduchý súbor, pomocou ktorého možno
            príslušnú službu (či služby) odstrániť zo serveru.</para>
          </listitem>
        </itemizedlist>

        <para>Vytvorme teda pre náš budúci meteorologický servis súbor
        nasadenia a odstránenia - <filename>deploy.wsdd</filename> a
        umiestnime ho do adresára <filename>WEB-INF</filename> našej webovej
        aplikácie.</para>

        <programlisting>&lt;deployment xmlns="http://xml.apache.org/axis/wsdd/" 
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt;

  &lt;service name="WeatherService" provider="java:RPC"&gt;  
    &lt;parameter name="className" value="axis.weather.WeatherServiceImpl"/&gt;
    &lt;parameter name="allowedMethods" value="*"/&gt;
    &lt;parameter name="wsdlPortType" value="WeatherService"/&gt;
    &lt;parameter name="wsdlTargetNamespace" value="urn:kb-ws-SiteWS"/&gt;
  &lt;/service&gt;

  &lt;beanMapping xmlns:ns="urn:axis-weather"
     qname="ns:Weather"
     type="java:axis.weather.Weather"
  /&gt;      
&lt;/deployment&gt;</programlisting>

        <para>Tento súbor nasadenia popisuje nasledovné záležitosti webovej
        služby:</para>

        <itemizedlist>
          <listitem>
            <para>webová služba sa volá
            <classname>WeatherService</classname></para>
          </listitem>

          <listitem>
            <para>trieda, ktorá bude implementovať túto webovú službu sa volá
            <classname>axis.weather.WeatherServiceImpl</classname></para>
          </listitem>

          <listitem>
            <para>vo webovej služby dávame klientom k dispozícii všetky metódy
            implementujúcej triedy (hviezdička v parametri
            <token>allowedMethods</token>). Ak nemienime zverejniť všetky
            metódy tried môžeme v tomto parametri uviesť explicitný zoznam
            názvov metód oddelený čiarkami.</para>
          </listitem>

          <listitem>
            <para>môžeme špecifikovať port a cieľový menný priestor pre
            vygenerovaný WSDL.</para>
          </listitem>

          <listitem>
            <para>element <token>beanmapping</token> špecifikuje, že fazuľa
            <classname>Weather</classname> bude mapovaná na XML pomocou
            axisovských nástrojov na serializáciu a deserializáciu
            fazúľ.</para>
          </listitem>
        </itemizedlist>
      </section>

      <section>
        <title id="server-config-intro">Konfiguračný súbor servera</title>

        <para><anchor id="server-config-intro" />Servlet Axisu načítava svoje
        konfiguračné nastavenia zo súboru
        <filename>server-config.wsdd</filename>. Tento súbor obsahuje
        všeobecné nastavenia pre Axis a principiálne dáta z popisovačov
        nasadenia všetkých webových služieb, ktoré sú na danom serveri k
        dispozícii.</para>

        <para>Konfiguračný súbor sa automaticky vygeneruje pri nasadení prvého
        webserviceu a pri nasadení ďalších webových služieb sa tiež
        automaticky upravuje.</para>
      </section>

      <section>
        <title>Nasadenie webovej služby</title>

        <para>Na nasadenie webovej služby potrebujeme mať pripravenú a
        skompilovanú implementáciu a popisovač nasadenia. Spustíme aplikáciu a
        presvedčíme sa, že nám beží servlet Axisu (v browseri skúsime
        navštívíť <ulink
        url="http://localhost:8080/wstest/servlet/AxisServlet">http://localhost:8080/wstest/servlet/AxisServlet</ulink>;
        mali by sme vidieť zoznam dosiaľ nasadených webových služieb).
        Korektný beh tohto servletu je dôležitý preto, lebo samotný proces
        nasadenia je implementovaný ako webová služba.</para>

        <para>Samotné nasadenie môžeme spraviť napr. pomocou nasledovného BAT
        súboru <filename>make-server-config.bat</filename>.</para>

        <para><programlisting>rem Vygeneruje server-config na zaklade deployment descriptora

call setaxiscp.bat

set AXIS_SERVLET_LOCATION=http://localhost:8080/wstest/services
set DEPLOYMENT_DESCRIPTOR=c:\projects\wstest\web\WEB-INF\deploy.wsdd

java -cp %AXISCLASSPATH%;. org.apache.axis.client.AdminClient -l%AXIS_SERVLET_LOCATION% %DEPLOYMENT_DESCRIPTOR%
</programlisting></para>

        <para>Axis by nám mal vypísať okrem nejakych ladiacich hlášok
        hlavne</para>

        <screen>Processing file c:\projects\wstest\web\WEB-INF\deploy.wsdd
&lt;Admin&gt;Done processing&lt;/Admin&gt;</screen>

        <para>Po návšteve URL AxisServletu (<ulink
        url="http://localhost:8080/wstest/services">http://localhost:8080/wstest/services</ulink>)
        by sme mali vidieť v zozname nasadených webových služieb aj náš
        <classname>WeatherService</classname> spolu s odkazom na WSDL a
        zoznamom poskytovaných metód (naša webová služba poskytuje 4
        metódy).</para>

        <figure>
          <title>Zoznam nasadených webových služieb v Axise</title>

          <mediaobject>
            <imageobject>
              <imagedata fileref="axisservlet.gif" />
            </imageobject>
          </mediaobject>
        </figure>
      </section>

      <section>
        <title>Testovanie webovej služby</title>

        <para>Opäť ako v prípade primitívnej webovej služby si môžeme z WSDL
        dokumentu vygenerovať klientské triedy.</para>

        <programlisting>java -cp %AXISCLASSPATH% org.apache.axis.wsdl.WSDL2Java --output . http://localhost:8080/wstest/services/WeatherService?wsdl</programlisting>

        <para>Samozrejme, pri generovaní klientských tried z webovej adresy
        musíme mať axisovskú webaplikáciu stále spustenú.</para>

        <para>Po vygenerovaní získame opäť štyri triedy:
        <classname>WeatherService</classname>,
        <classname>WeatherServiceService</classname>,
        <classname>WeatherServiceServiceLocator</classname> a
        <classname>WeatherServiceSoapBindingStu</classname>b. Z nich môžeme
        opäť jednoduchým spôsobom zostaviť testovaciu triedu.</para>

        <programlisting>package axis.weather.test;

import java.net.URL;
import java.util.Calendar;

import axis_wstest_WeatherService.WeatherService;
import axis_wstest_WeatherService.WeatherServiceServiceLocator;

public class WeatherServiceTest {

  public static void main(String[] args) throws Exception {
    WeatherServiceServiceLocator locator = new WeatherServiceServiceLocator();
    WeatherService ws = locator.getWeatherService(new URL("http://alhambra:8080/wstest/services/WeatherService"));
    
    // v mapovani XML typov na javovske typy je nekonzistencia: java.util.Date -&gt; xsd:dateTime -&gt; java.util.Calendar
    // to nam narusi jednoliatost tried.
    System.out.println(ws.getWeather(Calendar.getInstance()));
    System.out.println(ws.getServiceInfo());
    System.out.println(ws.getWeatherForToday().getTextStatus());    
  }
}</programlisting>

        <para>Až na zmienený zádrheľ <footnote>
            <para>Jeden spôsob, ako ho obísť, je popísaný na IBM
            developerWorks, <ulink
            url="http://www-128.ibm.com/developerworks/library/ws-tip-roundtrip1.html">http://www-128.ibm.com/developerworks/library/ws-tip-roundtrip1.html</ulink></para>
          </footnote>medzi mapovaným dátumových dátových typov by všetko malo
        prebehnúť v poriadku a mali by sme bez problémov získať výsledky z
        webovej služby.</para>
      </section>
    </section>

    <section>
      <title>Metóda top-down</title>

      <para>Ak máme vytvorené, či zhora dané WSDL, problematika klientskeho
      kódu je jednoduchá. Vygenerujeme ho pomocou nástroja
      <command>WSDL2Java</command> podobne ako v prípade, keď sme generovali
      klientské triedy k nášej instantnej webovej službe.</para>

      <para>Triedy pre server vieme tiež vygenerovať nástrojom
      <command>WSDL2JAVA</command>, ale s inou sadou parametrov.</para>

      <programlisting>call setaxiscp.bat

java -cp %AXISCLASSPATH%;. org.apache.axis.wsdl.WSDL2Java --server-side --output . http://localhost:8080/wstest/InstantWeatherService.jws?wsdl</programlisting>

      <para>Dôležitý je parameter <parameter>--server-side</parameter>, ten
      hovorí, že z WSDL sa majú vygenerovať nielen klientské, ale aj
      serverovské triedy. Po spustení tohto príkazu sa nám okrem klasických
      štyroch základných súborov vygeneruje súbor
      <filename>InstantWeatherServiceSoapBindingImpl.java</filename>. Ten
      reprezentuje triedu, ktorá obsahuje výkonný kód webovej služby. Napr.
      pre WSDL našej instantnej webovej služby sa nám vygeneroval nasledovný
      zdrojový kód pre implementačnú triedu.</para>

      <programlisting>/**
 * InstantWeatherServiceSoapBindingImpl.java
 *
 * This file was auto-generated from WSDL
 * by the Apache Axis 1.3 Aug 31, 2005 (10:05:42 GMT+00:00) WSDL2Java emitter.
 */

package localhost.wstest.InstantWeatherService_jws;

public class InstantWeatherServiceSoapBindingImpl implements localhost.wstest.InstantWeatherService_jws.InstantWeatherService{
    public float getCurrentTemperature(java.lang.String cityName) throws java.rmi.RemoteException {
        return -3;
    }

    public java.lang.String getCurrentWeather(java.lang.String cityName) throws java.rmi.RemoteException {
        return null;
    }

    public float[] getTemperaturePrediction(java.lang.String cityName, int howManyDays) throws java.rmi.RemoteException {
        return null;
    }

}</programlisting>

      <para>Do tejto triedy môžeme doplniť príslušný výkonný kód.</para>

      <para>Okrem príslušných súborov nám <command>WSDL2Java</command>
      vygeneroval aj deskriptor nasadenia webovej služby
      (<filename>deploy.wsdd</filename>) a odstránenie
      (<filename>undeploy.wsdd</filename>), ktorý môžeme použiť pri generovaní
      súboru <filename>server-config.wsdd</filename>.</para>
    </section>
  </section>

  <section>
    <title>Pokročilé techniky</title>

    <section>
      <title>Generovanie tried pomocou <command>WSDL2Java</command> do slušnej
      adresárovej štruktúry.</title>

      <para>Štandardne generuje <command>WSDL2JAVA</command> triedy do
      adresárových štruktúr, ktoré zodpovedajú menným priestorom vo WSDL.
      Často je ale požiadavkou generovať triedy do vopred daných adresárov. Na
      to slúži parameter <parameter>--fileNStoPkg</parameter>. Tento parameter
      udáva cestu k property súboru, v ktorom sú mapovania menných priestorov
      na balíčky v Jave. Použitie je potom nasledovné:</para>

      <programlisting>java -cp %AXISCLASSPATH%;. org.apache.axis.wsdl.WSDL2Java --output . --fileNStoPkg NStoPkg.properties <emphasis>adresaURL</emphasis> </programlisting>

      <para>Súbor <filename>NStoPkg.properties</filename> vyzerá napr.
      nasledovne:</para>

      <programlisting># 
# mapovanie namespaceov vo WSDL na packages v Jave pouzivane pri generovani klientskeho kodu z WSDL
#
http\://localhost\:8080/wstest/services/InstantWeatherService_jws=axis.wstest.ws</programlisting>

      <para>Tento súbor musí spĺňať všetky obmedzenia vlastné properties
      súborom jazyka Java. Musí byť uložený v kódovaní iso-8859-1 a nepovolené
      znaky musí mať ošetrené pomocou spätnej lomky (najčastejšie je problémom
      znak dvojbodky.</para>
    </section>

    <section>
      <title>Konfigurácia Axisu</title>

      <para>Konfigurácia Axisu prebieha na tradičných dvoch stranách: na
      serveri a v klientoch.</para>

      <section>
        <title>Konfigurácia servera</title>

        <para>Konfigurácia servera sa nachádza v XML súbore
        <filename>server-config.wsdd</filename>, ktorý sa nachádza v adresári
        <filename>WEB-INF</filename> webovej aplikácie. Ako sme spomínali v
        častiach o<link linkend="server-config-intro"> konfiguračnom súbore
        webservera</link>, tento súbor obsahuje informácie zo súborov
        <filename>deploy.wsdd</filename> nasadených webových služieb.</para>

        <para>Príklad konfiguračného súboru je napr.</para>

        <programlisting>&lt;deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt;
 &lt;globalConfiguration&gt;
  &lt;parameter name="sendMultiRefs" value="true"/&gt;
  &lt;parameter name="disablePrettyXML" value="true"/&gt;
  &lt;parameter name="adminPassword" value="admin"/&gt;
  &lt;parameter name="attachments.Directory" value="c:\Projects\WSTest\web\WEB-INF\attachments"/&gt;
  &lt;parameter name="dotNetSoapEncFix" value="true"/&gt;
  &lt;parameter name="enableNamespacePrefixOptimization" value="false"/&gt;
  &lt;parameter name="sendXMLDeclaration" value="true"/&gt;
  &lt;parameter name="sendXsiTypes" value="true"/&gt;
  &lt;parameter name="attachments.implementation" value="org.apache.axis.attachments.AttachmentsImpl"/&gt;
  &lt;requestFlow&gt;
   &lt;handler type="java:org.apache.axis.handlers.JWSHandler"&gt;
    &lt;parameter name="scope" value="session"/&gt;
   &lt;/handler&gt;
   &lt;handler type="java:org.apache.axis.handlers.JWSHandler"&gt;
    &lt;parameter name="scope" value="request"/&gt;
    &lt;parameter name="extension" value=".jwr"/&gt;
   &lt;/handler&gt;
  &lt;/requestFlow&gt;
 &lt;/globalConfiguration&gt;
 &lt;handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/&gt;
 &lt;handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/&gt;
 &lt;handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/&gt;
 &lt;service name="AdminService" provider="java:MSG"&gt;
  &lt;parameter name="allowedMethods" value="AdminService"/&gt;
  &lt;parameter name="enableRemoteAdmin" value="false"/&gt;
  &lt;parameter name="className" value="org.apache.axis.utils.Admin"/&gt;
  &lt;namespace&gt;http://xml.apache.org/axis/wsdd/&lt;/namespace&gt;
 &lt;/service&gt;
 &lt;service name="Version" provider="java:RPC"&gt;
  &lt;parameter name="allowedMethods" value="getVersion"/&gt;
  &lt;parameter name="className" value="org.apache.axis.Version"/&gt;
 &lt;/service&gt;
 &lt;transport name="http"&gt;
  &lt;requestFlow&gt;
   &lt;handler type="URLMapper"/&gt;
   &lt;handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/&gt;
  &lt;/requestFlow&gt;
  &lt;parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/&gt;
  &lt;parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/&gt;
  &lt;parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/&gt;
  &lt;parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/&gt;
  &lt;parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/&gt;
  &lt;parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/&gt;
 &lt;/transport&gt;
 &lt;transport name="local"&gt;
  &lt;responseFlow&gt;
   &lt;handler type="LocalResponder"/&gt;
  &lt;/responseFlow&gt;
 &lt;/transport&gt;
&lt;/deployment&gt;</programlisting>

        <para>Tento konfiguračný súbor ukazuje, že máme nasadené dve webové
        služby (AdminService a Version), dva režimy transportu (http a local).
        V úvode sú dôležité konfiguračné direktívy.</para>

        <para><note>
            <para>Z praktického hľadiska patrí medzi tie dôležitejšie
            direktíva <token>sendMultiRefs</token>. Tá má umožňovať
            optimalizáciu elementov v XML dokumente. (V prípade, že sa prenáša
            napr. pole, ktoré obsahuje 15 rovnakých prvkov sa pri zapnutej
            optimalizácii namiesto pätnásťnásobného duplikovania rovnakého
            elementu (ktorý môže byť pomerne rozsiahly) odošle pole s 15
            referenciami na daný prvok a ten sa potom uvedie v SOAP správe len
            raz.). Žiaľ, podľa našich skúseností je táto funkcia
            implementovaná nespoľahlivo a preto je pravdepodobne lepšie ju
            vypnúť (<token>nastaviť hodnotu sendMultirefs</token> na
            <token>false</token>).</para>
          </note></para>

        <note>
          <para>Bližší popis jednotlivých elementov WSDD súborov možno nájsť
          napr. na <ulink
          url="http://www.osmoticweb.com/axis-wsdd/">http://www.osmoticweb.com/axis-wsdd/</ulink></para>
        </note>
      </section>

      <section>
        <title>Konfigurácia klienta</title>

        <para>Konfiguráciu klienta riadi súbor, ktorý je analogický
        konfiguračnému súboru servera. Súbor sa nazýva
        <filename>client-config.wsdd</filename> a je ho potrebné uložiť do
        CLASSPATH klienta. Ak klient pri spustení nenájde v CLASSPATH
        užívateľov konfiguračný súbor, použije implicitnú verziu uloženú vo
        vnútri Axisu.</para>

        <para>Príklad konfiguračného súboru:</para>

        <programlisting>&lt;deployment name="defaultClientConfig"
            xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt;
 &lt;globalConfiguration&gt;
   &lt;parameter name="disablePrettyXML" value="true"/&gt;
   &lt;parameter name="sendMultiRefs" value="false"/&gt;
   &lt;parameter name="enableNamespacePrefixOptimization" value="false"/&gt;
 &lt;/globalConfiguration&gt;
 &lt;transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/&gt;
 &lt;transport name="local" pivot="java:org.apache.axis.transport.local.LocalSender"/&gt;
 &lt;transport name="java" pivot="java:org.apache.axis.transport.java.JavaSender"/&gt; 
&lt;/deployment&gt;</programlisting>

        <note>
          <para>Analogicky ako v prípade servera máme z praxe skúsenosti s
          nastavením direktívy <token>sendMultiRefs</token> na
          <token>false</token>.</para>
        </note>
      </section>
    </section>

    <section>
      <title>Vlastné mapovanie dátových typov</title>

      <para>Axis automaticky podporuje ,,od prírody" mapovanie rôznych
      dátových typov na XML. V praxi možno bez problémov využívať</para>

      <itemizedlist>
        <listitem>
          <para>primitívne dátové typy</para>
        </listitem>

        <listitem>
          <para>polia zložené z primitívnych dátových typov</para>
        </listitem>

        <listitem>
          <para>triedy zodpovedajúce odporúčaniu Java Beans obsahujúce vo
          svojich dátových členoch primitívne dátové typy a Java Beans</para>
        </listitem>

        <listitem>
          <para>niektoré ďalšie dátové typy z Javy
          (<classname>java.util.Date</classname>).</para>
        </listitem>
      </itemizedlist>

      <para>V praxi však niekedy nastane situácia, keď máme vlastnú triedu,
      ktorá sa namapuje na XML nesprávne, prípadne sa vôbec nenamapuje.
      Typickým prípadom sú objekty typu
      <classname>java.sql.Timestamp</classname>, ktoré sa serializujú v
      prípade kontajnera Tomcat správne. Problém však nastáva pri použití
      Axisu v aplikačnom serveri WebSphere. V ňom sa z neznámych dôvodov
      objekty serializujú a deserializujú nesprávne.<footnote>
          <para>Pri používaní Axisu vo WebSphere a deserializácii SOAP správy,
          ktorá obsahovala <classname>java.sql.Timestamp </classname>sme sa
          stretli so zaujímavou hláškou <computeroutput>Cannot serialize class
          long.</computeroutput></para>
        </footnote> V takomto prípade je potrebné si vytvoriť vlastné
      mapovanie objektov. To pozostáva z dvoch procesov:</para>

      <itemizedlist>
        <listitem>
          <para>mapovanie štruktúry triedy na XML schému (XSD)</para>
        </listitem>

        <listitem>
          <para>samotné mapovanie inštancií tried na XML v správe SOAP.</para>
        </listitem>
      </itemizedlist>

      <para>Samotný proces mapovania objektov je v Axise realizovaný pomocou
      dvoch párov tried: jeden pár (trieda a jej továreň) slúži na
      serializáciu objektu, druhý na deserializáciu.</para>

      <section>
        <title>Serializátor</title>

        <para>Príklad serializátora:</para>

        <programlisting>public class SQLTimestampSerializer implements SimpleValueSerializer {

          /* Získa stringovú reprezentáciu objektu, v tomto prípade volaním metódy toString().
           *  
           * @see org.apache.axis.encoding.SimpleValueSerializer#getValueAsString(java.lang.Object, org.apache.axis.encoding.SerializationContext)
           */
          public String getValueAsString(Object value, SerializationContext context) {
                    Timestamp timestamp = (Timestamp) value;
                    return Long.toString(timestamp.getTime());
                  }

          /* Serializuje timestamp do stringu pomocou metódy getValueAsString()
           *  
           * @see org.apache.axis.encoding.Serializer#serialize(javax.xml.namespace.QName, org.xml.sax.Attributes, java.lang.Object, org.apache.axis.encoding.SerializationContext)
           */
          public void serialize(QName name, Attributes attributes, Object value,
              SerializationContext context) throws IOException {

                    context.startElement(name, attributes);
                    context.writeString(getValueAsString(value, context));
                    context.endElement();
                  }

          /* Zapise prislusnu schemu do WSDL
           * @see org.apache.axis.encoding.Serializer#writeSchema(java.lang.Class, org.apache.axis.wsdl.fromJava.Types)
           */
          public Element writeSchema(Class javaType, Types types) throws Exception {
                    /*
                      Schema vyzera nasledovne:
                      &lt;complexType&gt;
                        &lt;all&gt;
                           &lt;element name="time" type="xsd:long/&gt;
                        &lt;/all&gt;
                      &lt;/complexType&gt;     
                    */

                    Element complexType = types.createElement("complexType");
                    Element sequence = types.createElement("all");
                    Element element = types.createElement("element");
                    element.setAttribute("name", "time");
                    element.setAttribute("type", "xsd:long");

                    complexType.appendChild(sequence);
                    sequence.appendChild(element);

                    return complexType;    
                  }

          /* 
           * @see javax.xml.rpc.encoding.Serializer#getMechanismType()
           */
          public String getMechanismType() {
                    return Constants.AXIS_SAX;
          }
}</programlisting>

        <para>Trieda zabezpečuje oba spomínané procesy: pomocou metódy
        <methodname>serialize</methodname> sa pomocou známych metód
        manipulácie s XML uzlami vloží do SOAP správy serializovaná podoba
        objektu. Metóda <methodname>writeSchema</methodname> zase zabezpečuje
        zápis XML schémy príslušnej triedy do WSDL dokumentu.</para>

        <para>Náš serializátor je primitívny. z objektu typu
        <classname>java.sql.Timestamp </classname>jednoducho serializuje jeho
        milisekundovú hodnotu (typu <classname>long</classname>) a uloží ju do
        SOAP správy ako reťazec.</para>

        <note>
          <para>Zdalo by sa, že prirodzenejším spôsobom je vytvoriť schému
          použitím elementov <token>restriction</token> a <token>base</token>
          v uložených v elemente <token>simpleType</token>. Zdá sa však, že
          Axis namapuje takýto ,,obmedzený" dátový typ na základný typ v Jave
          (bez ohľadu na to, že v schéme je definované obmedzenie). Napr
          <token>restriction</token> s bázou <token>xsd:long</token> sa
          namapuje priamo na <token>long</token>, to však asi nie je želaný
          efekt.</para>
        </note>
      </section>

      <section>
        <title>Deserializátor</title>

        <para>Deserializátor je jednoduchší. Nemusí sa totiž starať o tvorbu
        elementov pre schému triedy vo WSDL. Jeho úlohou je len samotná
        deserializácia XML a vytvorenie objektu z elementov v SOAP
        správe.</para>

        <para><programlisting>public class SQLTimestampDeserializer extends SimpleDeserializer {

          public SQLTimestampDeserializer(Class javaType, QName xmlType) {
                    super(javaType, xmlType);
                  }

          /* 
           * Konvertuje serializovanu hodnotu reprezentovanu Stringom na Timestamp.
           * 
           * @see org.apache.axis.encoding.ser.SimpleDeserializer#makeValue(java.lang.String)
           */
          public Object makeValue(String source) throws Exception {
                    long l = Long.parseLong(source);
                    return new Timestamp(l);
          }
}</programlisting>Jediná užitočná metóda konvertuje reťazec v správe na typ
        <classname>long</classname> a pomocou neho vytvorí inštanciu triedy
        <classname>Timestamp</classname>.</para>
      </section>

      <section>
        <title>Továrne pre serializátory a deserializátory.</title>

        <para>Každému serializátoru a deserializátoru zodpovedá jedna továreň.
        Našťastie, samotné továrne sú veľmi jednoduché. Navyše, továrne pre
        serializátory a deserializátory sú rovnaké, líšia sa len v názvoch a v
        názvoch tried, ktoré vytvárajú.</para>

        <programlisting>public class SQLTimestampDeserializerFactory extends BaseDeserializerFactory {

          public SQLTimestampDeserializerFactory(Class javaType, QName xmlType) {
                    super(SQLTimestampDeserializer.class, xmlType, javaType);
                  }
}</programlisting>
      </section>

      <section>
        <title>Konfigurácia serializácie a deserializácie na strane
        servera</title>

        <para>Po skompilovaní a sprístupnení tried serveru však ešte musíme
        upovedomiť Axis, že má na príslušné triedy používať náš vlastný
        serializátor. To urobíme pomocou elementu <token>typemapping</token>,
        ktorý môžeme vložiť buď do deskriptora nasadenia príslušnej webovej
        služby, alebo do konfiguračného súboru servera. Samozrejme nezabudneme
        v prípade potreby webovú službu nasadiť znovu.</para>

        <para>Príslušný element bude pre náš príklad vyzerať
        nasledovne:</para>

        <programlisting>&lt;typeMapping 
  xmlns:kb="urn:java-sql"
  qname="testws:timestamp"
   languageSpecificType="java:java.sql.Timestamp"
   serializer="novotnyr.ws.SQLTimestampSerializerFactory"
   deserializer="novotnyr.ws.SQLTimestampDeserializerFactory"
   encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
/&gt;</programlisting>

        <para>Ak sa všetko podarilo správne, môžeme vygenerovať pre nasadenú
        webovú službu vygenerovať klientské triedy. Dôkazom toho, že Axis na
        strane servera našiel naše vlastne serializátory a deserializátory, by
        malo byť zobrazenie WSDL pre príslušnú webovú službu v prehliadači. Ak
        je všetko v poriadku, vo zobrazenom WSDL by mala XML schéma elementu
        pre <classname>java.sql.Timestamp</classname> zodpovedať schéme, ktorú
        sme si skonštruovali v metóde <methodname>writeSchema</methodname> v
        serializátore.</para>
      </section>

      <section>
        <title>Konfigurácia serializácie a deserializácie na strane
        klienta</title>

        <para>Situácia na strane klienta je v tomto prípade komplikovanejšia.
        Pri generovaní tried na strane klienta totiž nie je možné nastaviť
        vlastné mapovanie typov. Axis automaticky pridelí triedam, ktoré nevie
        namapovať štandardný serializátor/deserializátor určený pre Java
        Beans. Žiaľ, tento postup je nesprávny - Java Beans serializátor totiž
        nevie interpretovať hodnoty, ktoré do SOAP správy uložil
        <classname>SQLTimestampSerializer</classname>.</para>

        <para>Preto je potrebné ručne vygenerovanú klientskú triedu upraviť
        ručne a nahradiť nesprávny serializátor/deserializátor korektnou
        verziou. V konštruktore triedy
        <classname>xxxxxxSoapBindingStub</classname> nájdeme kód, ktorý
        asociuje triedu <classname>java.sql.Timestamp</classname> s
        príslušnými továrňami. Korektná verzia kódu má byť</para>

        <programlisting>qName = new javax.xml.namespace.QName("urn:java-sql", "timestamp");
cachedSerQNames.add(qName);
cls = java.sql.Timestamp.class;
cachedSerClasses.add(cls);
cachedSerFactories.add(new SQLTimestampSerializerFactory(cls, qName));
cachedDeserFactories.add(new SQLTimestampDeserializerFactory(cls, qName));</programlisting>

        <para>Použitie klienta ostáva nezmenené.</para>
      </section>
    </section>
  </section>
</article>