<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>j2ee.pl - związani z javą &#187; ajax</title>
	<atom:link href="http://j2ee.pl/tag/ajax/feed/" rel="self" type="application/rss+xml" />
	<link>http://j2ee.pl</link>
	<description>związani z Javą</description>
	<lastBuildDate>Mon, 24 Aug 2009 11:45:27 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>DWR = Direct Web Remoting = ?</title>
		<link>http://j2ee.pl/2008/03/16/dwr-direct-web-remoting/</link>
		<comments>http://j2ee.pl/2008/03/16/dwr-direct-web-remoting/#comments</comments>
		<pubDate>Sun, 16 Mar 2008 17:17:03 +0000</pubDate>
		<dc:creator>Michał Mally</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[JSP]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[direct web remoting]]></category>
		<category><![CDATA[dwr]]></category>
		<category><![CDATA[java script]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://j2ee.pl/2008/03/16/dwr-direct-web-remoting/</guid>
		<description><![CDATA[Słowem wstępu
Mroczna historia na dobry początek
Przed monitorem jednego z terminali Biblioteki Uniwersyteckiej spędzałem kolejną dłużącą się bez końca godzinę. Na zewnątrz niebo nabierało coraz ciemniejszych barw. Poszukiwałem ciekawego tematu na nowy artykuł na j2ee.pl.
Wielogodzinna praca powodowała, że zaczynałem odczuwać nadchodzące zmęczenie. Postanowiłem podarować sobie chwilę odpoczynku od zagadnień IT. Nierozsądnie wpisałem w pasku przeglądarki tvn24.pl [...]]]></description>
			<content:encoded><![CDATA[<h2>Słowem wstępu</h2>
<h3>Mroczna historia na dobry początek</h3>
<p style="text-align: justify"><img src="http://getahead.org/images/dwr-logo-200.gif" style="float: left" />Przed monitorem jednego z terminali <em>Biblioteki Uniwersyteckiej</em> spędzałem kolejną dłużącą się bez końca godzinę. Na zewnątrz niebo nabierało coraz ciemniejszych barw. Poszukiwałem ciekawego tematu na nowy artykuł na <strong>j2ee.pl</strong>.</p>
<p style="text-align: justify">Wielogodzinna praca powodowała, że zaczynałem odczuwać nadchodzące zmęczenie. Postanowiłem podarować sobie chwilę odpoczynku od zagadnień IT. Nierozsądnie wpisałem w pasku przeglądarki <em>tvn24.pl</em> &#8211; strona chwilę się wczytywała. Wziąłem ostatni już łyk <em>Red Bulla</em>. Jak przez mgłę spoglądałem na treści atakujące moją bezbronną świadomość. W prawym dolnym rogu ujrzałem umieszczoną sondę. Zupełnie bez powodu ogarnęła mnie nagła, nieodparta ochota na to, żeby zagłosować&#8230; Lecz nagle poczułem na skórze powiew mroźnego powietrza, który w parnej sali <em>Biblioteki</em> wydawał się pojawić jakby znikąd. To co działo się następnie nie sposób opisać jakimikolwiek słowami. Przez chwilę zdawało mi się, że tuż za moimi plecami bezgłośnie niczym rycerz ciemności przemknął <strong>Premier Jarosław Kaczyński</strong> cenzurując swym wzrokiem oglądane przeze mnie treści. Modliłem się, żeby to wszystko nie było prawdą &#8211; nie chciałem, żeby Premier zobaczył oglądania jakich <em>pornograficznych</em> treści dopuszczają się studenci. Wciąż oszołomiony tym, co wydarzyło się przed chwilą postanowiłem zaczerpnąć świeżego powietrza.</p>
<p><span id="more-115"></span></p>
<p style="font-size: 0.8em; color: #aaaaaa">(Aby przejść do części praktycznej kliknij <a href="#czesc_praktyczna">tutaj</a>.)</p>
<h3>DWR?</h3>
<p style="text-align: justify">Po krótkiej przerwie powróciłem pełen energii do dalszej pracy.  Początkowo miałem kilka pomysłów, aby w końcu zdecydować się na przedstawienie frameworka <strong>Direct Web Remoting</strong>, czyli jak twierdzą jego autorzy łatwego sposobu na <em>Ajax</em> dla programistów <em>Javy</em>. Przyznam się, że dałem się im przekonać. W jakim jednak celu tworzyć kolejne wprowadzenie do tak dobrze znanego i jeszcze lepiej udokumentowanego szkieletu programistycznego. To zupełnie tak jakby dzisiaj pisać kolejną książkę o podstawowych zagadnieniach <em>Springa</em>, czy <em>Hibernate</em>&#8216;a. Jest na <em>sali</em> dzisiaj ktokolwiek, kto by nie miał do czynienia z bardzo dobrymi publikacjami opisującymi te zagadnienia?&#8230; Tak właśnie myślałem!</p>
<h3>A może jednak?</h3>
<p style="text-align: justify">Mimo tych szargających mną wątpliwości postanowiłem dokonać krótkiej prezentacji frameworka <strong>DWR</strong>, gdyż zachowując wszystkie podobieństwa do wyżej wymienionych technologii – z <strong>DWR</strong> wydaje się być jednak nieco inaczej. Ja osobiście bliżej przyjrzeć się temu frameworkowi miałem dopiero okazję stosunkowo niedawno. Z badań opinii publicznej ;), jakie pozwoliłem sobie przeprowadzić okazało się, że nie jestem jedyną osobą, która do niedawana o <strong>DWR</strong> niewiele wiedziała – a nawet jeżeli wiedziała, to była to wiedza czysto teoretyczna.</p>
<h3>DWR + Spring</h3>
<p style="text-align: justify">Aktualną stabilną <em>gałęzią</em> jest <em>branch</em> <strong>2.0.x</strong>, ale trwają intensywne prace nad wersją <strong>3.0</strong>, która już niebawem powinna ujrzeć światło dzienne. W tym artykule będę korzystał z ostatniej dostępnej wersji stabilnej, czyli na dzień dzisiejszy – <strong>2.0.3</strong>. Swoją uwagę skupię przede wszystkim na możliwościach integracji tego frameworka ze <em>Springiem</em>. Po tym nieco teoretycznym wstępie przedstawię działający przykład aplikacji webowej zwracając uwagę na szczególnie interesujące i pomocne elementy z punktu widzenia developera. Przedstawione rozwiązanie będzie miało charakter <em>Proof of Concept</em>, stąd też przykład którym się posłużę będzie wyjątkowo trywialny.</p>
<h3>Cel</h3>
<p style="text-align: justify">Gdy inwestuje się w coś swój (czy też czyjś) czas warto wiedzieć, co właściwie ma być finalnym efektem włożonej pracy. Moim celem będzie pokazanie w jaki sposób umożliwić bezpośrednie wywoływania z poziomu <em>JavaScript</em> metod serwisów zdefiniowanych (jako <em>beany</em>) w kontekście aplikacyjnym Springa.</p>
<h3>Narzędzia, frameworki i biblioteki wykorzystane do wykonania tego ćwiczenia</h3>
<ul>
<li> 		Narzędzia
<ul>
<li><strong>Eclipse/JDT</strong></li>
<li><strong>maven2</strong> &#8211; posłuży nam do budowania, <em>deployowania</em> oraz testowania aplikacji</li>
<li><strong>maven-jetty-plugin</strong> – wtyczka do <em>mavena</em> pozwalająca na uruchamianie serwera aplikacji, jakim jest <em>Jetty</em> z poziomu <em>mavena</em> (bardzo przyjemne oraz efektywne narzędzie)</li>
</ul>
</li>
<li> 		Framworki i biblioteki
<ul>
<li><strong>Spring Framework</strong></li>
<li><strong>DWR</strong></li>
<li><strong>jQuery</strong></li>
</ul>
</li>
</ul>
<p><a title="czesc_praktyczna" name="czesc_praktyczna"></a></p>
<h2>Część praktyczna</h2>
<p style="font-size: 0.8em; color: #aaaaaa">(Aby pobrać pełne źródła tworzonej aplikacji kliknij <a href="http://j2ee.pl/wp-content/uploads/2008/03/dwr-example-src.zip" title="j2ee.pl DWR example source">tutaj</a>.)</p>
<h3>Przygotowanie standardowego <em>szablonu</em> aplikacji <em>webowej</em></h3>
<p style="text-align: justify">Rozpocznę od utworzenia pliku <em>pom.xml</em> z myślą o aplikacji sieciowej korzystającej ze <em>Spring Web MVC</em>. Z pewnością robiłeś już takie rzeczy wiele razy.</p>
<pre class="prettyprint">pom.xml:
&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;

  &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
  &lt;groupId&gt;pl.jcommerce.example&lt;/groupId&gt;
  &lt;artifactId&gt;dwr-example&lt;/artifactId&gt;
  &lt;version&gt;0.1&lt;/version&gt;
  &lt;packaging&gt;war&lt;/packaging&gt;
  &lt;name&gt;j2ee.pl DWR example&lt;/name&gt;

  &lt;build&gt;
    &lt;defaultGoal&gt;package&lt;/defaultGoal&gt;
    &lt;finalName&gt;dwr-example&lt;/finalName&gt;
    &lt;resources&gt;
      &lt;resource&gt;
        &lt;directory&gt;src/main/webapp/WEB-INF&lt;/directory&gt;
      &lt;/resource&gt;
    &lt;/resources&gt;
    &lt;plugins&gt;
      &lt;plugin&gt;
        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
        &lt;configuration&gt;
          &lt;encoding&gt;utf8&lt;/encoding&gt;
          &lt;source&gt;1.6&lt;/source&gt;
          &lt;target&gt;1.6&lt;/target&gt;
        &lt;/configuration&gt;
      &lt;/plugin&gt;
      &lt;plugin&gt;
        &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
        &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
        &lt;configuration&gt;
          &lt;connectors&gt;
            &lt;connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"&gt;
              &lt;port&gt;8080&lt;/port&gt;
              &lt;maxIdleTime&gt;60000&lt;/maxIdleTime&gt;
            &lt;/connector&gt;
          &lt;/connectors&gt;
          &lt;scanIntervalSeconds&gt;5&lt;/scanIntervalSeconds&gt;
          &lt;webAppConfig&gt;
            &lt;contextPath&gt;/dwr-example&lt;/contextPath&gt;
          &lt;/webAppConfig&gt;
        &lt;/configuration&gt;
      &lt;/plugin&gt;
    &lt;/plugins&gt;
  &lt;/build&gt;

  &lt;pluginRepositories&gt;
    &lt;pluginRepository&gt;
      &lt;id&gt;mortbay-repo&lt;/id&gt;
      &lt;name&gt;mortbay-repo&lt;/name&gt;
      &lt;url&gt;http://jetty.mortbay.org/maven2/release&lt;/url&gt;
      &lt;snapshots&gt;
        &lt;enabled&gt;true&lt;/enabled&gt;
      &lt;/snapshots&gt;
    &lt;/pluginRepository&gt;
  &lt;/pluginRepositories&gt;

  &lt;dependencies&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
      &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
      &lt;version&gt;2.5&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
      &lt;artifactId&gt;jstl&lt;/artifactId&gt;
      &lt;version&gt;1.2&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-core&lt;/artifactId&gt;
      &lt;version&gt;2.5.2&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;
      &lt;version&gt;2.5.2&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-web&lt;/artifactId&gt;
      &lt;version&gt;2.5.2&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
      &lt;version&gt;2.5.2&lt;/version&gt;
    &lt;/dependency&gt;

  &lt;/dependencies&gt;

&lt;/project&gt;</pre>
<p style="text-align: justify">Element, który może być dla Ciebie nowy i być może powinieneś na niego zwrócić uwagę, to konfiguracja wtyczki <em>Jetty</em>. Jeżeli nie znałeś tego narzędzia do tej pory, to z czystym sumieniem mogę Ci je polecić.</p>
<p style="text-align: justify">Teraz utworzę standardową dla <em>mavena</em> strukturę katalogów i dodam kolejne ważne z punktu widzenia projektu pliki.</p>
<pre class="prettyprint">web.xml:
&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4"&gt;

  &lt;display-name&gt;j2ee.pl DWR example&lt;/display-name&gt;
  &lt;description&gt;&lt;/description&gt;

  &lt;listener&gt;
    &lt;display-name&gt;Spring context-loader&lt;/display-name&gt;
    &lt;listener-class&gt;
      org.springframework.web.context.ContextLoaderListener
    &lt;/listener-class&gt;
  &lt;/listener&gt;

  &lt;context-param&gt;
    &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
    &lt;param-value&gt;
      /WEB-INF/applicationContext.xml
    &lt;/param-value&gt;
  &lt;/context-param&gt;

  &lt;servlet&gt;
    &lt;servlet-name&gt;dispatcher&lt;/servlet-name&gt;
    &lt;servlet-class&gt;
      org.springframework.web.servlet.DispatcherServlet
    &lt;/servlet-class&gt;
    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;/servlet&gt;

  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;dispatcher&lt;/servlet-name&gt;
    &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;

  &lt;filter&gt;
    &lt;filter-name&gt;CharacterEncodingFilter&lt;/filter-name&gt;
    &lt;filter-class&gt;
      org.springframework.web.filter.CharacterEncodingFilter
    &lt;/filter-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;encoding&lt;/param-name&gt;
      &lt;param-value&gt;UTF-8&lt;/param-value&gt;
    &lt;/init-param&gt;
  &lt;/filter&gt;

  &lt;filter-mapping&gt;
    &lt;filter-name&gt;CharacterEncodingFilter&lt;/filter-name&gt;
    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;

&lt;/web-app&gt;</pre>
<p style="text-align: justify">Następnie wykonam polecenie:</p>
<pre class="prettyprint">mvn eclipse:eclipse -DdownloadSources=true</pre>
<p style="text-align: justify">Po pomyślnym wykonaniu tego polecenia zostaną ściągnięte wszystkie wymagane pakiety wraz ze źródłami oraz zostaną utworzone pliki projektu <em>Eclipse</em>, co umożliwi nam pracę z poziomu <em>IDE</em>. Istnieje wiele innych sposobów integracji <em>Eclipse</em> z projektem <em>maven2</em>, ale żaden z nich nie działa do końca perfekcyjnie. W tym jednak wypadku wtyczka <em>eclipse</em> do <em>mavena</em> zdaje egzamin.</p>
<p style="text-align: justify">Teraz już z poziomu <em>IDE</em> uzupełnie konfigurację dodając pliki <em>applicationContext.xml</em> oraz <em>dispatcher-servlet.xml</em> nie zawierające na razie jednak żadnych definicji <em>beanów</em>.</p>
<h3>Pierwsze uruchomienie aplikacji</h3>
<p style="text-align: justify">Jeżeli wszystko poszło zgodnie z planem, to po wykonaniu polecenia</p>
<pre class="prettyprint">mvn jetty:run</pre>
<p>powinnienem być w stanie wywołać z przeglądarki adres <em>http://localhost:8080/dwr-example/</em>.</p>
<p style="text-align: justify">Warto zauważyć, że począwszy od tej chwili po każdej zmianie dokonanej przeze mnie w plikach projektu <em>Jetty</em> automatycznie dokona ponownego <em>deploymentu</em> aplikacji. Jeżeli zmiana plików projektu nie wywoła takiego działania najprawdopodobniej należy dodać dodatkowe wpisy do elementu konfiguracyjnego wtyczki <em>Jetty</em> o nazwie <em>scanTargets</em>.</p>
<p style="text-align: justify">Aplikacja owszem działa, ale trudno na razie być zachwyconym jej funkcjonalnością.</p>
<h3>Dodanie serwisu do aplikacji</h3>
<p style="text-align: justify">Dodam teraz 3 pliki <em>java</em>, które będą definiowały serwis, który będę chciał w przyszłości <em>wołać</em> z poziomu <em>JavaScriptu</em>. Posłużę się prostym serwisem, który będzie posiadał zaledwie jedną metodę. Do określonego przeze mnie celu będzie to zupełnie wystarczające.</p>
<pre class="prettyprint">
package pl.jcommerce.example.dwr.service;

import pl.jcommerce.example.dwr.model.Product;

/**
 * @author Michal Mally
 */
public interface ProductService {

  public Product getProductByCode(String code);

}</pre>
<pre class="prettyprint">
package pl.jcommerce.example.dwr.service;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import pl.jcommerce.example.dwr.model.Product;

/**
 * @author Michal Mally
 */
public class ProductServiceDummyImpl implements
    ProductService {

  private static final Set&lt;Product&gt; products;

  private static final Random random = new Random();

  static {
    products = new HashSet&lt;Product&gt;();
    products.add(new Product("CSS",
        "CSS: The Missing Manual", "O'Reilly Media", 3,
        BigDecimal.valueOf(2309, 2)));
    products.add(new Product("JQR", "jQuery in Action",
        "Manning Publications", 4, BigDecimal.valueOf(2639,
            2)));
    products.add(new Product("DWR",
        "Practical DWR 2 Projects", "Apress", 0, BigDecimal
            .valueOf(3101, 2)));
  }

  @Override
  public Product getProductByCode(String code) {
    for (final Product product : products) {
      if (product.getCode().equals(code.toUpperCase())) {
        return product;
      }
    }

    try {
      Thread.sleep(random.nextInt(1000));
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    return null;
  }

}</pre>
<pre class="prettyprint">
package pl.jcommerce.example.dwr.model;

import java.math.BigDecimal;

/**
 * @author Michal Mally
 */
public class Product {

  private String code;

  private String name;

  private String producer;

  private int itemsAvailable;

  private BigDecimal price;

  public Product() {
    // intentionally left blank
  }

  public Product(String code, String name, String producer,
      int itemsAvailable, BigDecimal price) {
    super();
    this.code = code;
    this.name = name;
    this.producer = producer;
    this.itemsAvailable = itemsAvailable;
    this.price = price;
  }

  // (...) getters/setters

}</pre>
<p style="text-align: justify">Ponieważ całość będę uruchamiał lokalnie w implementacji serwisu celowo zostało dodane losowe opóźnienie, aby móc symulować realne warunki, jakie mają miejsce w Internecie. Mianowicie często zdarza się sytuacja, że odpowiedzi na wysyłane żądanie nie przychodzą w tej samej kolejności w jakiej zostały wysłane żądania.</p>
<p style="text-align: justify">Dodam teraz odpowiedni wpis do pliku <em>applicationContext.xml</em>.</p>
<pre class="prettyprint">
  &lt;bean id="productService" class="pl.jcommerce.example.dwr.service.ProductServiceDummyImpl"&gt;
  &lt;/bean&gt;</pre>
<h3>A gdzie ten DWR?</h3>
<p style="text-align: justify">Będąc w tym miejscu dodanie wsparcia dla DWR zajmie mi już zaledwie kilka minut.</p>
<p style="text-align: justify">Do pliku <em>web.xml</em> dodaje definicję <em>servletu</em> DWR oraz odpowiednie do niego mapowanie.</p>
<pre class="prettyprint">
  &lt;servlet&gt;
    &lt;servlet-name&gt;dwr&lt;/servlet-name&gt;
    &lt;servlet-class&gt;org.directwebremoting.spring.DwrSpringServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;debug&lt;/param-name&gt;
      &lt;param-value&gt;true&lt;/param-value&gt;
    &lt;/init-param&gt;
  &lt;/servlet&gt;

  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;dwr&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/dwr/*&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;</pre>
<p style="text-align: justify">Servlet <em>DWR</em> skonfigurowałem w taki sposób, aby mieć możliwość <em>debugowania</em>.</p>
<p style="text-align: justify">Teraz ponownie modyfikuje plik <em>applicationContext.xml</em> doprowadzając go do finalnej postaci.</p>
<pre class="prettyprint">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
	xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://www.directwebremoting.org/schema/spring-dwr
      http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd"&gt;

  &lt;dwr:configuration&gt;
    &lt;dwr:convert type="bean" class="pl.jcommerce.example.dwr.model.Product"/&gt;
  &lt;/dwr:configuration&gt;

  &lt;bean id="productService" class="pl.jcommerce.example.dwr.service.ProductServiceDummyImpl"&gt;
    &lt;dwr:remote javascript="ProductService"&gt;
      &lt;dwr:include method="getProductByCode"/&gt;
    &lt;/dwr:remote&gt;
  &lt;/bean&gt;

&lt;/beans&gt;</pre>
<p style="text-align: justify">Do zdefiniowanego już wcześniej <em>beana</em> <strong>productService</strong> dodaję tag <em>dwr:remote</em> określając, że <em>bean</em>, którego to dotyczy ma być dostępny z poziomu <em>JavaScriptu</em> pod nazwą <em>ProductService</em> oraz, że interesuje mnie wywoływanie metody <em>getProductByCode()</em>.</p>
<p style="text-align: justify">Nie można także zapomnieć o tagu <em>dwr:configuration</em>, który jest wymagany niezależnie od tego, czy zawiera jakieś dodatkowe elementy. W moim przypadku określam, że klasa <em>Product</em> będzie transformowana z/do obiektu <em>JavaScript</em> przy użyciu dostępnych w niej <em>getterów</em>/<em>setterów</em>.</p>
<p style="text-align: justify">Pamiętać należy również o dodaniu nowej zależności do pliku <em>pom.xml</em>. Po tej operacji należy także ponownie wywołać <em>mvn eclipse:eclipse -DdownloadSources=true</em>.</p>
<pre class="prettyprint">
    &lt;dependency&gt;
      &lt;groupId&gt;org.directwebremoting&lt;/groupId&gt;
      &lt;artifactId&gt;dwr&lt;/artifactId&gt;
      &lt;version&gt;2.0.2&lt;/version&gt;
    &lt;/dependency&gt;</pre>
<h3>Testowanie DWR przy pomocy dostarczonych wraz z frameworkiem narzędzi</h3>
<p style="text-align: justify">Przyszedł czas na sprawdzenie, czy wszystko się powiodło. Wpisuję w przeglądarce adres <em>http://localhost:8080/dwr-example/dwr/</em>. Moim oczom ukazuje się lista znanych klas dla <strong>DWR</strong>:</p>
<pre class="prettyprint">
ProductService (pl.jcommerce.example.dwr.service.ProductServiceDummyImpl)</pre>
<p style="text-align: justify">Klikam na <em>ProductService</em> i uzyskuję dzięki temu dostęp do bardzo przyjemnego narzędzie pozwalającego na przetestowanie wywołania serwisu. Odnajduję metodę <em>getProductByCode()</em>, wpisuje jako argument jej wywołania <em>&#8220;CSS&#8221;</em> i klikam <em>Execute</em>.</p>
<p style="text-align: justify">W wyskakującym okienku (<em>alert()</em>) uzyskuje dokładne informacje o produkcie o kodzie <em>CSS</em>. Zatem wywołanie się powiodło :). Warto zauważyć, że także na konsoli <em>Jetty</em> widać wpis o wykonanym zapytaniu.</p>
<h3>Tworzenie strony HTML korzystającej z DWR</h3>
<p style="text-align: justify">Ostatnim już elementem tego <em>tutoriala</em> będzie utworzenie strony <em>HTML</em> umożliwiającej wyszukiwanie produktów po ich kodach bez konieczności przeładowania strony. Przykład trywialny, ale w tym wypadku wystarczający.</p>
<p style="text-align: justify">Ponieważ zamierzam korzystać z <em>JavaScriptu</em> wspomogę się biblioteką <strong>jQuery</strong>, która znacznie to ułatwia. Bibliotekę umieszczam w folderze <em>/src/main/webapp/script/js/</em>. Polecam bliższe zapoznanie się z tą biblioteką <em>JS</em>.</p>
<p style="text-align: justify">A teraz już źródło samej strony JSP oraz krótki opis tego, co się wewnątrz jej źródła dzieje.</p>
<pre class="prettyprint">
&lt;%@ page pageEncoding="UTF-8" %&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;

&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"&gt;
  &lt;head&gt;
    &lt;title&gt;Product search&lt;/title&gt;

    &lt;script type="text/javascript" src="${pageContext.request.contextPath}/script/js/jquery-1.2.3.min.js"&gt;&lt;/script&gt;

	&lt;script type='text/javascript' src='${pageContext.request.contextPath}/dwr/interface/ProductService.js'&gt;&lt;/script&gt;
	&lt;script type='text/javascript' src='${pageContext.request.contextPath}/dwr/engine.js'&gt;&lt;/script&gt;

    &lt;script type="text/javascript"&gt;
    	// &lt;![CDATA[
    	var lastRequestId = 0;

    	function onProductReceived(product) {
    		if (product != null) {
    			$("#productDescription").show();
    			$("#invalidCode").hide();

    			$("#productCode").html(product.code);
    			$("#productName").html(product.name);
    			$("#productProducer").html(product.producer);
    			$("#productItemsAvailable").html(product.itemsAvailable);
    			$("#productPrice").html(product.price);

    			return;
    		}

    		$("#invalidCode").show();
    		$("#productDescription").hide();
    	}

    	$(document).ready(
    		function(){
    			$("#productDescription").hide();
    			$("#invalidCode").hide();
    			$("#productCodeInput").keyup(
    				function() {
    					var requestId = ++lastRequestId;

    					ProductService.getProductByCode($(this).val(), {
	    						callback: function(product) {
	    							if (requestId != lastRequestId) {
	    								return;
	    							} 

			    					onProductReceived(product);
			    				}
		    				}
		    			)
    				}
    			);
    		}
    	);
    	// ]]&gt;
    &lt;/script&gt;

  &lt;/head&gt;
  &lt;body&gt;
	&lt;p&gt;Enter product code &lt;input id="productCodeInput" type="text"/&gt;&lt;/p&gt;
	&lt;div id="productDescription"&gt;
		&lt;p&gt;Product code: &lt;span id="productCode"&gt;&lt;/span&gt;&lt;/p&gt;
		&lt;p&gt;Product name: &lt;span id="productName"&gt;&lt;/span&gt;&lt;/p&gt;
		&lt;p&gt;Producer: &lt;span id="productProducer"&gt;&lt;/span&gt;&lt;/p&gt;
		&lt;p&gt;Items available: &lt;span id="productItemsAvailable"&gt;&lt;/span&gt;&lt;/p&gt;
		&lt;p&gt;Price: &lt;span id="productPrice"&gt;&lt;/span&gt;&lt;/p&gt;
	&lt;/div&gt;
	&lt;div id="invalidCode"&gt;
		&lt;p&gt;&lt;strong&gt;You have chosen invalid code! Please choose another one.&lt;/strong&gt;&lt;/p&gt;
	&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
<p style="text-align: justify">W bloku <em>head</em> załączam źródło biblioteki <em>jQuery</em> oraz kod wygenerowany przez <strong>DWR</strong> będący naszym punktem dostępu do serwisu <em>ProductService</em>. Trochę niżej definiujemy funkcję <em>onProductChange</em>, która będzie wywoływana w momencie uzyskania odpowiedzi z serwisu. Jej zadaniem będzie podmiana części informacji na stronie zgodnie z aktualnie wprowadzonym przez użytkownika kodem produktu.</p>
<p style="text-align: justify">Zapytanie do serwisu wysyłam każdorazowo po tym jak zajdzie zdarzenie zwolnienia przez użytkownika klawisza. W <em>prawdziwych</em> aplikacjach całkowicie odradzam tą technikę, jednak dla celów tego artykułu jest ona wystarczająca.</p>
<p style="text-align: justify">Ponieważ użytkownik może z dosyć dużą częstotliwością powodować wysłanie żądań do serwisu musimy się zabezpieczyć przed sytuacją w której  nieaktualna odpowiedź nadpisze aktualną wartość. Do tego posłuży nam zmienna <em>requestId</em>. Przy każdorazowym odebraniu odpowiedzi z serwisu będziemy sprawdzali, czy nie zostało przypadkiem wysłane nowsze żądanie. W takim przypadku starsza odpowiedź zostanie przeze mnie zignorowana.</p>
<p style="text-align: justify">Gdybym nie potrzebował stosować wyżej opisanej techniki wywołanie serwisu mogłoby wyglądać jak poniżej.</p>
<pre class="prettyprint">ProductService.getProductByCode($(this).val(), onProductChanged);</pre>
<p style="text-align: justify">Aby całość dostępna była poprzez adres <em>http://localhost:8080/dwr-example/product/search.html</em> uzupełnię jeszcze plik <em>dispatcher-servlet.xml</em>.</p>
<pre class="prettyprint">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"&gt;

  	&lt;bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
  	  &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /&gt;
  	  &lt;property name="prefix" value="/WEB-INF/jsp/" /&gt;
  	  &lt;property name="suffix" value=".jsp" /&gt;
  	&lt;/bean&gt;

  	&lt;bean name="/product/search.html" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" /&gt;

&lt;/beans&gt;</pre>
<h2>Podsumowanie</h2>
<h3>Nieprzekonany?</h3>
<p style="text-align: justify"><strong>DWR</strong> jest dojrzałym frameworkiem. W tym artykule główną uwagę skupiłem na jego prostocie wykorzystywania w kontekście aplikacji webowej budowanej na <em>Springu</em>. Pokazałem zaledwie od czego nasza <em>zabawa</em> z <strong>DWR</strong> może się rozpocząć. Dla projektantów i programistów bardzo ważnym aspektem natomiast jest elastyczność rozwiązań z których się korzysta oraz możliwość łatwego ich dostosowania do własnych potrzeb. Przeglądając oficjalną dokumentację szybko można się zorientować, że <strong>DWR</strong> w tej dziedzinie stara się spełniać stawiane mu oczekiwania.</p>
<p style="text-align: justify">Nie poruszyłem w tym artykule żadnego zaawansowanego aspektu <strong>DWR</strong>, ale wierzę, że jeżeli zdecydujesz się na korzystanie z tego frameworka, to dzięki dobrze napisanej dokumentacji nie będziesz miał problemu z jego dostosowaniem.</p>
<h3>Odnośniki</h3>
<ol>
<li><a href="http://getahead.org/dwr">DWR</a></li>
<li><a href="http://jquery.com/">jQuery</a></li>
<li><a href="http://springframework.org/">Spring Framework</a></li>
<li><a href="http://www.mortbay.org/">Jetty</a></li>
<li><a href="http://maven.apache.org/">Maven</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/03/16/dwr-direct-web-remoting/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>GWT, czyli AJAX bez bólu</title>
		<link>http://j2ee.pl/2007/09/30/gwt-czyli-ajax-bez-bolu/</link>
		<comments>http://j2ee.pl/2007/09/30/gwt-czyli-ajax-bez-bolu/#comments</comments>
		<pubDate>Sun, 30 Sep 2007 00:59:49 +0000</pubDate>
		<dc:creator>Andrzej Maliszewski</dc:creator>
				<category><![CDATA[GWT]]></category>
		<category><![CDATA[ajax]]></category>

		<guid isPermaLink="false">http://j2ee.pl/2007/09/30/gwt-czyli-ajax-bez-bolu/</guid>
		<description><![CDATA[Google Web Toolkit jest to framework umożliwiający łatwe i bezbolesne tworzenie aplikacji AJAX. Aby użyć GWT wystarcza znajomość Javy i podstaw HTMLa, nie jest natomiast potrzebna znajomość JavaScriptu. Poniższy artykuł ma na celu przybliżyć podstawy GWT oraz krok po kroku pokazać jak szybko stworzyć prostą aplikację opartą na tym frameworku.

Budowa GWT
Na początek przyjrzymy się, co [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://code.google.com/images/code_sm.png" alt="Google Code" />Google Web Toolkit jest to framework umożliwiający łatwe i bezbolesne tworzenie aplikacji AJAX. Aby użyć GWT wystarcza znajomość Javy i podstaw HTMLa, nie jest natomiast potrzebna znajomość JavaScriptu. Poniższy artykuł ma na celu przybliżyć podstawy GWT oraz krok po kroku pokazać jak szybko stworzyć prostą aplikację opartą na tym frameworku.<br />
<span id="more-90"></span></p>
<h3>Budowa GWT</h3>
<p>Na początek przyjrzymy się, co tak naprawdę kryje się nazwą GWT. GWT składa się z czterech podstawowych komponentów.</p>
<p>Pierwszy z nich, kompilator Java-to-JavaScript, tłumaczy kod źródłowy na JavaScript i HTML. Oczywiście, aby kod mógł zostać poprawnie przetłumaczony, musi spełniać szereg ograniczeń. Przede wszystkim należy pamiętać, że GWT w obecnej wersji (1.4) jest zgodny z Javą 1.4.2,  więc użycie jakichkolwiek rozszerzeń Javy 1.5 skończy się błędem kompilacji. Poza tym należy zapomnieć o takich przyjemnościach jak wielowątkowość czy refleksja.</p>
<p>Drugi komponent to dedykowana przeglądarka WWW. Umożliwia ona uruchomienie aplikacji bez translacji do JavaScriptu, w tzw. Hosted Mode. Szczegóły – za chwilę.</p>
<p>Trzeci komponent to biblioteka emulująca podstawowe klasy biblioteki standardowej Javy w JavaScripcie. Zawiera ona implementację większości klas z <code>java.lang</code>, oraz część <code>java.util</code>.</p>
<p>Czwarty komponent, czyli biblioteka Web UI, służy do tworzenia interfejsu użytkownika który zostanie następnie przetłumaczony na stronę WWW.</p>
<h3>Hosted Mode vs. Web Mode</h3>
<p>GWT oferuje nam dwa tryby uruchomienia stworzonej aplikacji. Pierwszy z nich, tzw. Hosted Mode, wykorzystując wspomnianą już dedykowaną przeglądarkę WWW uruchamia aplikację bezpośrednio w wirtualnej maszynie Javy, bez tłumaczenia kodu do JavaScriptu. To pozwala programiście korzystać z takich dobrodziejstw Javy, jak chociażby wygodne debugowanie kodu. Z tego względu Hosted Mode jest używany w trakcie procesu tworzenia aplikacji.</p>
<p>Z kolei Web Mode polega na skompilowaniu kodu do HTMLa i JavaScriptu i uruchomieniu tak przygotowanej aplikacji w dowolnej przeglądarce WWW. Ten tryb używany jest przy wdrażaniu gotowej aplikacji.</p>
<h3>GWT w praktyce</h3>
<p>Po krótkim wprowadzeniu czas przejść do konkretów, czyli przykładowej aplikacji.  Będzie to prosty kalendarz zamieszczony na stronie WWW.<br />
Aby użyć GWT wystarczy pobrać wersję odpowiednią dla używanego systemu ze strony <a href="http://code.google.com/webtoolkit">http://code.google.com/webtoolkit</a> i rozpakować ją do dowolnego katalogu. Dla wygody warto dopisać go do zmiennej środowiskowej <code>PATH</code>.</p>
<p>Do stworzenia projektu skorzystamy z dwóch skryptów dostarczonych wraz z GWT: <code>projectCreator</code> oraz <code>applicationCreator</code>. Pierwszy z nich posłuży do stworzenia plików projektu Eclipse’a, drugi do wygenerowania podstawowych plików aplikacji.</p>
<pre class="prettyprint">C:\\projects\\gwtcalendar&gt;projectCreator.cmd -eclipse gwtcalendar
Created directory C:\\projects\\gwtcalendar\\src
Created directory C:\\projects\\gwtcalendar\\test
Created file C:\\projects\\gwtcalendar\\.project
Created file C:\\projects\\gwtcalendar\\.classpath

C:\\projects\\gwtcalendar&gt;applicationCreator.cmd -eclipse gwtcalendar pl.jcommerce.example.gwtcalendar.client.Calendar
Created directory C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar
Created directory C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar\\client
Created directory C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar\\public
Created file C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar\\Calendar.gwt.xml
Created file C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar\\public\\Calendar.html
Created file C:\\projects\\gwtcalendar\\src\\pl\\jcommerce\\example\\gwtcalendar\\client\\Calendar.java
Created file C:\\projects\\gwtcalendar\\Calendar.launch
Created file C:\\projects\\gwtcalendar\\Calendar-shell.cmd
Created file C:\\projects\\gwtcalendar\\Calendar-compile.cmd</pre>
<p>Skrypt applicationCreator tworzy strukturę pakietów zgodną z zaleceniami GWT:<br />
<code>.../gwtcalendar</code> – główny pakiet zawierający wszystkie pliki projektu. Bezpośrednio w tym pakiecie tworzony jest plik konfiguracyjny XML.<br />
<code>.../gwtcalendar/client</code> – pakiet zawierający pliki źródłowe warstwy klienta. To właśnie te pliki będą później tłumaczone na JavaScript.<br />
<code>.../gwtcalendar/public</code> – w tym pakiecie zawarte są statyczne zasoby takie jak gotowe pliki html, css czy grafika.<br />
W typowych aplikacjach występuje dodatkowo pakiet <code>.../gwtcalendar/server</code>, zawierający źródła warstwy serwerowej. Ta część kodu nie podlega kompilacji do JavaScriptu, więc nie obowiązują tu żadne ograniczenia nałożone przez GWT. W naszej przykładowej aplikacji zajmiemy się tylko warstwą klienta, więc ten pakiet jest zbędny.</p>
<p>Dodatkowo stworzone zostały trzy pliki w katalogu głównym projektu:<br />
<code>Calendar.launch</code> – plik projektu Eclipse’a pozwalający uruchomić projekt w trybie debug<br />
<code>Calendar-shell.cmd</code> – skrypt uruchamiający aplikację w Hosted Mode<br />
<code>Calendar-compile.cmd</code> – skrypt kompilujący aplikację do postaci HTMLa i JavaScriptu w celu uruchomienia jej w trybie Web Mode</p>
<p>Po wykonaniu powyższych skryptów importujemy projekt do Eclipse’a i przystępujemy do dokładniejszych oględzin plików w pakiecie <code>.../gwtcalendar</code>.<br />
Najpierw przyjrzyjmy się plikowi konfiguracyjnemu modułu <code>Calendar.gwt.xml</code>:</p>
<pre class="prettyprint">&lt;module&gt;

   &lt;!-- Inherit the core Web Toolkit stuff.                  --&gt;
   &lt;inherits name='com.google.gwt.user.User'/&gt;

   &lt;!-- Specify the app entry point class.                   --&gt;
   &lt;entry-point class='pl.jcommerce.example.gwtcalendar.client.Calendar'/&gt;

&lt;/module&gt;</pre>
<p>Jak widać nie jest on zbyt skomplikowany. Pierwszy element, <code>inherits</code>, wskazuje na dziedziczenie ze standardowego modułu interfejsu użytkownika GWT. Drugi element, <code>entry-point</code>, wskazuje na główną klasę modułu. W momencie wczytania strony WWW, wykonana zostanie metoda <code>onModuleLoad()</code> wskazanej klasy. Główna klasa modułu musi implementować interfejs <code>com.google.gwt.core.client.EntryPoint</code>.</p>
<p>Teraz przejdźmy do naszej statycznej strony WWW, czyli pliku <code>Calendar.html</code>.  Wygenerowana, przykładowa zawartość pokazuje, jak można użyć modułu GWT jako część istniejącej strony WWW. W naszym przypadku w zupełności wystarczy mocno okrojona wersja:</p>
<pre class="prettyprint">&lt;html&gt;
   &lt;head&gt;

      &lt;title&gt;Calendar&lt;/title&gt;

   &lt;/head&gt;
   &lt;body&gt;

      &lt;script language='javascript' src='pl.jcommerce.example.gwtcalendar.Calendar.nocache.js'&gt;&lt;/script&gt;

      &lt;!-- OPTIONAL: include this if you want history support --&gt;
      &lt;iframe src="javascript:''" id="__gwt_historyFrame" style="width:0;height:0;border:0"&gt;&lt;/iframe&gt;

   &lt;/body&gt;
&lt;/html&gt;</pre>
<p>Nie ma tu za wiele do wyjaśniania. W elemencie <code>script</code> podajemy nazwę modułu z końcówką <code>.nocache.js</code>. Z kolei umieszczenie elementu <code>iframe</code> pozwala na wykorzystanie wsparcia historii przeglądarki oferowanego przez GWT. W naszej przykładowej aplikacji nie ma to żadnego zastosowania, ale warto pamiętać o tym udogodnieniu.</p>
<p>Na koniec trzeci, najbardziej interesujący plik, czyli <code>Calendar.java</code>. Podobnie jak w przypadku <code>Calendar.html</code>, wygenerowana treść metody <code>onModuleLoad()</code> jest tylko przykładowa i od razu możemy się jej pozbyć.</p>
<p>Przyjrzyjmy się teraz budowie interfejsu użytkownika za pomocą GWT. Podstawową klasą interfejsu jest <code>com.google.gwt.user.client.ui.Widget</code>. Z Widget dziedziczą wszystkie elementy UI takie jak przyciski, pola tekstowe, tabele i wiele innych. Szczególnym elementem jest <code>com.google.gwt.user.client.ui.Panel</code>, abstrakcyjna klasa bazowa dla wszystkich kontenerów. GWT, w odróżnieniu od np. Swinga, nie używa odrębnych klas typu <code>LayoutManager</code>. Układem komponentów zarządzają  bezpośrednio klasy dziedziczące z Panel, takie jak <code>HorizontalPanel</code>, <code>VerticalPanel</code>, <code>FlowPanel</code> czy <code>DockPanel</code>. Aby umieścić jakiekolwiek komponenty na stronie musimy uzyskać dostęp do głównego panelu. Służy do tego metoda statyczna <code>RootPanel.get()</code>.</p>
<p>Interfejs naszego kalendarza  będzie się składał z nagłówka zawierającego przyciski do zmiany wyświetlanego miesiąca, paska z etykietami oznaczającymi kolejne dni tygodnia oraz panelu przedstawiającego dni miesiąca. Na początek kilka zmiennych i stałych wykorzystywanych w naszej klasie, oraz metoda <code>onModuleLoad()</code>:</p>
<pre class="prettyprint">private int currYear;private int currMonth;
private VerticalPanel mainPanel;
private Label monthLabel;
private Widget monthPanel;private static final int DAY = 1000 * 3600 * 24;
private static final int WEEK = DAY * 7;
private static final String[] dayNames = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

/**
 * This is the entry point method.
 */
public void onModuleLoad() {
   Date date = new Date();
   currYear = date.getYear();
   currMonth = date.getMonth();
   initUI();
}</pre>
<p>Zmienne <code>mainPanel</code>, <code>monthLabel</code> oraz <code>monthPanel</code> będą przechowywać komponenty, do których potrzebny będzie dostęp w metodach odpowiedzialnych za „dynamikę” naszej aplikacji. Reszta kodu nie wymaga chyba komentarza. Przyjrzyjmy się wobec tego kolejnej metodzie tworzącej interfejs:</p>
<pre class="prettyprint">private void initUI() {
   mainPanel = new VerticalPanel();
   mainPanel.setWidth("250px");
   mainPanel.setSpacing(3);
   mainPanel.add(createHeaderPanel());
   mainPanel.add(createDayNamesPanel());
   mainPanel.add(monthPanel = createMonthPanel());
   RootPanel.get().add(mainPanel);
}</pre>
<p>W tej metodzie tworzymy główny panel, ustawiamy jego parametry i dodajemy podstawowe elementy. Na koniec ustawiamy go jako jedyny element RootPanela. Warto zwrócić uwagę na wywołanie metody <code>setWidth()</code>, która przyjmuje parametr typu <code>String</code>, a nie <code>int</code>. Metoda ta przyjmuje wszystkie poprawne wartości CSS, a więc poprawne będzie zarówno „250px”, jak i „5cm” czy „15%”.</p>
<p>Następne dwie metody tworzą nagłówek naszego kalendarza:</p>
<pre class="prettyprint">private Widget createHeaderPanel() {
   HorizontalPanel headerPanel = new HorizontalPanel();
   headerPanel.setWidth("100%");
   headerPanel.setSpacing(3);
   Button prevButton = new Button("&amp;lt;&amp;lt;");
   prevButton.setWidth("100%");
   headerPanel.add(prevButton);
   headerPanel.setCellWidth(prevButton, "20%");
   monthLabel = new Label((currYear + 1900) + "-" + (currMonth + 1));
   headerPanel.add(monthLabel);
   headerPanel.setCellHorizontalAlignment(monthLabel,
         HasHorizontalAlignment.ALIGN_CENTER);
   Button nextButton = new Button("&amp;gt;&amp;gt;");
   nextButton.setWidth("100%");
   headerPanel.add(nextButton);
   headerPanel.setCellWidth(nextButton, "20%");
   return headerPanel;
}

private Widget createDayNamesPanel() {
   HorizontalPanel dayNamesPanel = new HorizontalPanel();
   dayNamesPanel.setWidth("100%");
   dayNamesPanel.setSpacing(3);
   Label lab;
   for (int i = 0; i &lt; dayNames.length; i++) {
      lab = new Label(dayNames[i]);
      lab.setWidth("100%");
      dayNamesPanel.add(lab);
      dayNamesPanel.setCellWidth(lab, "14%");
      dayNamesPanel.setCellHorizontalAlignment(lab, HasHorizontalAlignment.ALIGN_CENTER);
   }
   return dayNamesPanel;
}</pre>
<p>Jak widać uzyskanie żądanego układu komponentów nie powinno sprawić problemów programiście posiadającemu chociaż podstawowe doświadczenia z takimi bibliotekami GUI jak Swing czy SWT. Bardzo pomocne są metody <code>setCellWidth()</code> czy <code>setCellHorizontalAlignment()</code>, pozwalające ustawić rozmiar i ułożenie osobno dla każdego elementu.<br />
Kolejne dwie metody tworzą panel przedstawiający dni miesiąca:</p>
<pre class="prettyprint">private Widget createMonthPanel() {
   VerticalPanel monthPanel = new VerticalPanel();
   monthPanel.setWidth("100%");
   monthPanel.setSpacing(3);
   Date firstDay = new Date(currYear, currMonth, 1);
   Date currDay = new Date(firstDay.getTime() - firstDay.getDay() * DAY);
   monthPanel.add(getWeekPanel(currDay));
   while ((currDay = new Date(currDay.getTime() + WEEK)).getMonth() == firstDay.getMonth()) {
      monthPanel.add(getWeekPanel(currDay));
   }
   return monthPanel;
}

private Widget getWeekPanel(Date date) {
   HorizontalPanel weekPanel = new HorizontalPanel();
   weekPanel.setWidth("100%");
   weekPanel.setSpacing(3);
   Date firstDay = new Date(date.getTime() - date.getDay() * DAY);
   for (int i = 0; i &lt; 7; i++) {
      Date btnDate = new Date(firstDay.getTime() + i * DAY);
      Button btn = new Button(String.valueOf(btnDate.getDate()));
      if (btnDate.getMonth() != currMonth) {
         btn.setEnabled(false);
      }
      btn.setWidth("100%");
      weekPanel.add(btn);
      weekPanel.setCellWidth(btn, "14%");
   }
   return weekPanel;
}</pre>
<p>W powyższych metodach nie ma w zasadzie niczego nowego, poza nieco karkołomnym żonglowaniem datami. Jeśli zastanawiasz się, dlaczego konsekwentnie używamy oznaczonych jako deprecated metod klasy <code>Date</code>, zamiast użyć znacznie wygodniejszej klasy <code>Calendar</code>, odpowiedź brzmi: klasa <code>Calendar</code> nie istnieje. A właściwie: klasa <code>Calendar</code> należy do tej znaczącej części pakietu <code>java.util</code>, której GWT nie implementuje. Pozostaje więc zacisnąć zęby i radzić sobie z tym, co mamy.</p>
<p>Ale wróćmy do naszej aplikacji. Mamy już wszystko co potrzeba do stworzenia interfejsu. Efekt możemy obejrzeć uruchamiając nasz projekt (bezpośrednio za pomocą Eclipse’a lub za pomocą skryptu <code>Calendar-shell.cmd</code>).</p>
<p>Teraz, gdy mamy stworzony interfejs, czas tchnąć życie w naszą stronę WWW. Do obsługi zdarzeń GWT wykorzystuje wzorzec Listener, dobrze znany np. ze Swinga. Na początek dodajmy interfejs <code>com.google.gwt.user.client.ui.ClickListener</code> do deklaracji naszej klasy <code>Calendar</code>:</p>
<pre class="prettyprint">public class Calendar implements EntryPoint, ClickListener {
   ...
}</pre>
<p>Następnie dopiszmy parę metod odpowiedzialnych za obsługę zdarzeń:</p>
<pre class="prettyprint">public void onClick(Widget sender) {
   String text = ((Button) sender).getText();
   if ("&lt;&lt;".equals(text)) {
      prevMonth();
   } else if ("&gt;&gt;".equals(text)) {
      nextMonth();
   } else {
      Window.alert(text + "-" + (currMonth + 1) + "-" + (currYear + 1900));
   }
}

private void prevMonth() {
   if (currMonth &gt; 0) {
      currMonth--;
   } else {
      currYear--;
      currMonth = 11;
   }
   refreshMonthPanel();
}

private void nextMonth() {
   if (currMonth &lt; 11) {
      currMonth++;
   } else {
      currYear++;
      currMonth = 0;
   }
   refreshMonthPanel();
}

private void refreshMonthPanel() {
   monthLabel.setText((currYear + 1900) + "-" + (currMonth + 1));
   mainPanel.remove(monthPanel);
   mainPanel.add(monthPanel = createMonthPanel());
}</pre>
<p>Na koniec musimy podpiąć obsługę kliknięć do stworzonych przycisków:</p>
<pre class="prettyprint">private Widget createHeaderPanel() {
   ...
   Button prevButton = new Button("&amp;lt;&amp;lt;");
   prevButton.setWidth("100%");
   <strong>prevButton.addClickListener(this);</strong>
   ...
   Button nextButton = new Button("&amp;gt;&amp;gt;");
   nextButton.setWidth("100%");
   <strong>nextButton.addClickListener(this);</strong>
   ...
}

private Widget getWeekPanel(Date date) {
   ...
   for (int i = 0; i &lt; 7; i++) {
      ...
      Button btn = new Button(String.valueOf(btnDate.getDate()));
      <strong>btn.addClickListener(this);</strong>
   ...
}</pre>
<p>W ten sposób dotarliśmy do końca, nasza przykładowa aplikacja jest gotowa. Wystarczy uruchomić skrypt <code>Calendar-compile.cmd</code>, a w katalogu WWW pojawi się nasza aplikacja przetłumaczona na HTML i JavaScript, gotowa do uruchomienia w dowolnej przeglądarce WWW lub umieszczenia na serwerze.</p>
<h3>Podsumowanie</h3>
<p>Oczywiście zaprezentowana aplikacja pokazuje tylko skromny wycinek możliwości GWT. Nie wspomnieliśmy o prostej komunikacji z serwerem za pomocą RPC, ułatwionej internacjonalizacji czy bezpośrednich wstawkach JavaScript za pomocą JavaScript Native Interface (JSNI). Jednak już tak prosty przykład pokazuje jak łatwo można tworzyć dynamiczne strony WWW używając jedynie Javy. Jeśli dodatkowo wierzyć zapewnieniom autorów o kompatybilności stworzonego kodu ze wszystkimi wiodącymi przeglądarkami WWW oraz o wydajności porównywalnej z ręcznie tworzonymi stronami AJAX, wówczas warto bliżej zainteresować się frameworkiem Google Web Toolkit.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2007/09/30/gwt-czyli-ajax-bez-bolu/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Prototype</title>
		<link>http://j2ee.pl/2007/08/07/prototype/</link>
		<comments>http://j2ee.pl/2007/08/07/prototype/#comments</comments>
		<pubDate>Tue, 07 Aug 2007 07:23:26 +0000</pubDate>
		<dc:creator>Grzegorz Dzierwa</dc:creator>
				<category><![CDATA[JS]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[JSP]]></category>

		<guid isPermaLink="false">http://j2ee.pl/2007/08/07/prototype/</guid>
		<description><![CDATA[Prototype jest biblioteką JavaScript, której autorem jest Sam Stephenson. Została ona opracowana po to by uprościć tworzenie skryptów mających wprowadzić dynamikę na stronach stronach www. Dzięki rozszerzeniu modelu DOM oraz wsparciu dla technologii Ajax budowa bogatego interfejsu użytkownika w aplikacjach webowych zajmie nam mniej czasu.

Sama biblioteka to jeden plik, który możemy szybko dodać do projektu. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://prototypejs.org" target="_blank"><img src="http://j2ee.pl/wp-content/uploads/2007/08/logo-home.gif" alt="PrototypeJS" />Prototype</a> jest biblioteką JavaScript, której autorem jest Sam Stephenson. Została ona opracowana po to by uprościć tworzenie skryptów mających wprowadzić dynamikę na stronach stronach www. Dzięki rozszerzeniu modelu DOM oraz wsparciu dla technologii Ajax budowa bogatego interfejsu użytkownika w aplikacjach webowych zajmie nam mniej czasu.</p>
<p><span id="more-68"></span></p>
<p>Sama biblioteka to jeden plik, który możemy szybko dodać do projektu. Wystarczy zaimportować jeszcze jeden plik ze skryptami JavaScript i gotowe.</p>
<p><strong>Rozszerzanie modelu DOM</strong></p>
<p>A teraz kilka słów na temat ciekawego pomysłu twórcy opisywanej biblioteki, czyli rozszerzania modelu DOM. Na czym ono polega i  co nam właściwie daje? Otóż, chodzi o to byśmy przy odnoszeniu się do elementów strony www wykorzystywali funkcję $(). Funkcja ta przyjmuje jako argument identyfikator danego elementu i zwraca referencję do obiektu &#8216;Element&#8217;, który opakowuje standardowy obiekt zwracany przez document.getElementById(). Owo opakowanie polega na rozszerzeniu zbioru metod, jakie   możemy wywoływać o te zawarte w <a href="http://prototypejs.org/api/element/methods" target="_blank">Element.Methods</a> i <a href="http://prototypejs.org/api/element/methods/simulated" target="_blank">Element.Methods.Simulated</a>. Od tej pory mamy dostęp do dodatkowych funkcji, które możemy wywołać na pobranym elemencie.</p>
<p>Zacznijmy może od prostego przykładu, w którym zobaczymy jak działa owo rozszerzanie modelu DOM przez <a href="http://prototypejs.org" target="_blank">Prototype</a>.</p>
<pre class="prettyprint">
&lt;head&gt;
 &lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"/&gt;
 &lt;script src="ścieżka/do/prototype.js" type="text/javascript"&gt;&lt;/script&gt;
 &lt;script type="text/javascript" &gt;
  function powitaj() {
   alert( $('welcomeSection').innerHTML );
  }
  function ukryj(obj) {
   $(obj).hide();
  }
  function pokaz(obj) {
   $(obj).show();
  }
 &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;span id="secretSection"&gt;Tę sekcję możemy ukryć&lt;/span&gt;
 &lt;button onclick="ukryj('secretSection')"&gt;Ukryj&lt;/button&gt;
 &lt;button onclick="pokaz('secretSection')"&gt;Pokaż&lt;/button&gt;
 &lt;div id="welcomeSection"&gt;Witaj&lt;/div&gt;
 &lt;button onclick="powitaj()"&gt;Powitaj&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Powyższy przykład pokazuje w jak prosty sposób możemy uzyskać dostęp do szerokiej gammy dodatkowych funkcji oferowanych przez <a href="http://prototypejs.org" target="_blank">Prototype</a>. Wykorzystując funkcję $() pobieramy referencję do elementów o identyfikatorach &#8216;welcomeSection&#8217; i &#8217;secretSection&#8217; i wywołujemy na nich funkcje z biblioteki <a href="http://prototypejs.org" target="_blank">Prototype</a>. W przypadku funkcji powitaj() odnosimy się jedynie do własności innerHTML. Nazwy zaprezentowanych funkcji mówią same za siebie. Funkcje hide() i show() kolejno ukrywają i pokazują sekcję &#8217;secretSection&#8217;. Owo ukrywanie i pokazywanie polega na zmianie stylu pobranego elementu. My jednak nie musimy tego wiedzieć. Po prostu wywołujemy funkcje na danym elemencie i uzyskujemy spodziewany efekt. By osiągnąć to samo bez wykorzystania <a href="http://prototypejs.org" target="_blank">Prototype</a> musielibyśmy napisać taki oto fragment:</p>
<pre class="prettyprint">
document.getElementById('secretSection').style.display = 'none';</pre>
<p>Wykorzystując prezentowaną bibliotekę wystarczy, że napiszemy:</p>
<pre class="prettyprint">
$('secretSection').hide();</pre>
<p>Proste. Genialne ;)</p>
<p>Gdy przy pomocy funkcji $() pobierzemy jeden z elementów formularza (input, textarea, select) to wówczas mamy do dyspozycji nieco więcej funkcji, które możemy wywołać. Dzieje się tak dlatego gdyż zwracany obiekt jest dodatkowo rozszerzany o metody z <a href="http://prototypejs.org/api/form/element" target="_blank">Form.Element.Methods</a>. Natomiast jeśli pobierzemy formularz (form) to rozszerzenie nastąpi o metody z <a href="http://prototypejs.org/api/form/element" target="_blank">Form.Methods</a>. Są to funkcje umożliwiające np. deaktywowanie pól formularza, czy też czyszczenie ich zawartości. Jednak gdy spróbujemy je wywołać na elemencie nie będącym formularzem, bądź jednym z jego pól to wówczas zostaniemy poinformowani o błędzie.</p>
<pre class="prettyprint">
&lt;script type="text/javascript" &gt;
 function deaktywuj(id) {
  $(id).disable();
 }
 function aktywuj(id) {
  $(id).enable();
 }
&lt;/script&gt;
&lt;div id="opis"&gt;&lt;h2&gt;Formularz danych osobowych&lt;/h2&gt;&lt;/div&gt;
&lt;form action="ex2.html" id="form1"&gt;
 &lt;label&gt;Imię: &lt;/label&gt;&lt;input type="text" id="imie"/&gt;&lt;br&gt;
 &lt;label&gt;Hobby: &lt;/label&gt;&lt;textarea rows="5" cols="20" id="hobby"&gt;&lt;/textarea&gt;&lt;br&gt;
 &lt;input type="submit" value="Zapisz" id="zapisz"/&gt;
&lt;/form&gt;

&lt;button onclick="deaktywuj('imie')"&gt;Deaktywuj&lt;/button&gt;
&lt;button onclick="aktywuj('imie')"&gt;Aktywuj&lt;/button&gt;
&lt;button onclick="deaktywuj('form1')"&gt;Deaktywuj formularz&lt;/button&gt;
&lt;button onclick="deaktywuj('opis')"&gt;Test błędu&lt;/button&gt;</pre>
<p>Przykład przedstawia prosty formularz zawierający dwa pola do wprowadzenia danych oraz przycisk powodujący wysłanie tychże danych do serwera. Ponadto na stronie znajdują się przyciski modyfikujące formularz. Dwa pierwsze, &#8216;Deaktywuj&#8217; i &#8216;Aktywuj&#8217; modyfikują stan pola &#8216;imie&#8217; wykorzystując funkcje &#8216;aktywuj&#8217; oraz &#8216;deaktywuj&#8217;. Każda z nich otrzymuje identyfikator elementu, który pozwoli na jego &#8220;wydobycie&#8221; przy pomocy funkcji $(). Pobrany obiekt opakowany jest w metody z Element.Methods, Element.Methods.Simulated oraz Form.Element.Methods. Metoda disable/enable, którą następnie wywołujemy jest funkcją zdefiniowaną w Form.Element.Methods. Nieco inaczej mechanizm rozszerzania modelu DOM zachowuje sie gdy przekazujemy identyfikator formularza &#8216;form1&#8242;. Funkcja $() zwraca obiekt posiadający metody z Element.Methods, Element.Methods.Simulated oraz Form.Methods. Teraz wywołujemy metodę disable/enable zdefiniowaną w Form.Methods, która modyfikuje wszystkie elementy formularza, a nie jak poprzednia tylko jeden.</p>
<p>Funcja $() ma inne, ciekawe właściwości o czym zaraz napiszę. Po pierwsze może ona przyjmować jako argument również obiekt zwrócony przez document.getElementById() i podobnie jak wcześniej zwróci nam obiekt typu &#8216;Element&#8217;. Jeśli natomiast podamy w wywołaniu funkcji kilka identyfikatorów to jako rezultat jej działania otrzymamy tablicę obiektów &#8216;Element&#8217;. Ciekawym pomysłem jest też możliwość potokowego wywoływania metod na obiektach. Jako, że spora liczba funkcji zwraca referencję do obiektu, który modyfikuje nic nie stoi na przeszkodzie by od razu wywołać na nim kolejną metodę. Oto prosty przykład:</p>
<pre class="prettyprint">
&lt;div id="topSecret" style="display:none;"&gt;Ten tekst jest niewidoczny.&lt;/div&gt;
&lt;script type="text/javascript"&gt;
 $('topSecret').update('Tajne dane juz nie są tajne.').show();
&lt;/script&gt;</pre>
<p>Pomimo tego, że po zastosowaniu funkcji $() mamy do naszej dyspozycji całkiem pokaźny zbiór funkcji ułatwiający modyfikację elementów, czy też prostsze poruszanie się po drzewie DOM <a href="http://prototypejs.org" target="_blank">Prototype</a> daje nam możliwość rozszerzenia tegoż zbioru o nasze własne metody. Posługując się funkcją Element.addMethods() możemy dołączyć nasze, opracowane w pocie czoła, metody. Musimy jedynie pamiętać by jako pierwszy argument przyjmowały obiekt, na którym będą operować. Jeśli dodatkowo będziemy zwracać jego referencję to tym samym zapewnimy możliwość potokowego wywoływania naszych funkcji. Oto przykład:</p>
<pre class="prettyprint">
&lt;script type="text/javascript"&gt;
 var MyMethods = {
  showValue: function(element){
   alert( $(element).innerHTML );
   return element;
  },
  setDefaultValue: function(element, value){
   return $(element).update(value);
  }
 }

 Element.addMethods(MyMethods);

 function testNaszychMetod() {
  $('newsContent').setDefaultValue('Nowa treść').showValue();
 }
&lt;/script&gt;</pre>
<p><strong>AJAX</strong></p>
<p>W tej części artykułu zobaczymy w jaki sposób <a href="http://prototypejs.org" target="_blank">Prototype</a> może nam ułatwić stosowanie technologii Ajax w naszym projekcie.</p>
<p>Zakładam, że osoba czytająca ten artykuł ma zainstalowanego mavena ponieważ wykorzystamy to narzędzie do budowy aplikacji webowej. Tak więc na początek stwórzmy katalog w wybranej przez siebie lokalizacji i wywołajmy w nim komendę</p>
<pre class="prettyprint">
mvn archetype:create
 -DgroupId=pl.j2ee.prototypejs
 -DartifactId=prototypejs
 -DarchetypeArtifactId=maven-archetype-webapp</pre>
<p>Powinna się ona zakończyć komunikatem BUILD SUCCESSFUL, a wynikiem jej działania będzie katalog o nazwie prototypejs zawierający strukturę aplikacji webowej odpowiednią dla mavena. Gdy będziemy wywoływać polecenie mvn install maven zbuduje nam wersję aplikacji gotową do uruchomienia na serwerze. Do uruchomienia prezentowanych przykładów wykorzystamy serwer <a href="http://tomcat.apache.org/download-55.cgi" target="_blank">tomcat w wersji 5.5</a>. Wróćmy może do naszego projektu. Poleceniem archetype:create utworzyliśmy szkielet aplikacji, który teraz powinniśmy nieco zmodyfikować. Na początek dodamy kilka wpisów do pom.xml dotyczących zależności, zewnętrznego repozytorium oraz buildu. Nasz plik pom.xml powinien wyglądać tak:</p>
<pre class="prettyprint">
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
 &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
 &lt;groupId&gt;pl.j2ee.prototype&lt;/groupId&gt;
 &lt;artifactId&gt;prototypejs&lt;/artifactId&gt;
 &lt;packaging&gt;war&lt;/packaging&gt;
 &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
 &lt;name&gt;prototypejs Maven Webapp&lt;/name&gt;
 &lt;url&gt;http://maven.apache.org&lt;/url&gt;
 &lt;dependencies&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;junit&lt;/groupId&gt;
   &lt;artifactId&gt;junit&lt;/artifactId&gt;
   &lt;version&gt;3.8.1&lt;/version&gt;
   &lt;scope&gt;test&lt;/scope&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;tomcat&lt;/groupId&gt;
   &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
   &lt;version&gt;5.5.15&lt;/version&gt;
  &lt;/dependency&gt;
 &lt;/dependencies&gt;
 &lt;build&gt;
  &lt;finalName&gt;prototypejs&lt;/finalName&gt;
  &lt;plugins&gt;
   &lt;plugin&gt;
    &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
    &lt;configuration&gt;
     &lt;source&gt;1.5&lt;/source&gt;
     &lt;target&gt;1.5&lt;/target&gt;
    &lt;/configuration&gt;
   &lt;/plugin&gt;
  &lt;/plugins&gt;
 &lt;/build&gt;
 &lt;repositories&gt;
  &lt;repository&gt;
   &lt;name&gt;ibiblio&lt;/name&gt;
   &lt;id&gt;ibiblio&lt;/id&gt;
   &lt;url&gt;http://www.ibiblio.org/maven/&lt;/url&gt;
  &lt;/repository&gt;
 &lt;/repositories&gt;
&lt;/project&gt;</pre>
<p>Jako że nasza aplikacja ma prezentować wykorzystanie technologii Ajax to musimy przygotować serwlet, który będzie generował odpowiedź dla żądań ajaxowych.<br />
W katalogu prototypejs\src\main\ utwórzymy dodatkowy katalog o nazwie java a w nim w pakiecie pl.j2ee.prototypejs.servlet klasę serwletu DataServlet. Docelowo będzie on obsługiwał żądania typu get i post.</p>
<pre class="prettyprint">
package pl.j2ee.prototypejs.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.Date;

public class DataServlet extends HttpServlet {
 public void doGet(HttpServletRequest req,
  HttpServletResponse res)
  throws ServletException, IOException {
  PrintWriter out = res.getWriter();

  out.println("Wiadomosc pobrana o " + new Date());

  String osoba = (String) req.getParameter("osoba");
  if (osoba != null)
   out.println( " Osoba: " + osoba);

  out.close();
 }

 public void doPost(HttpServletRequest req,
  HttpServletResponse res)
  throws ServletException, IOException {
   PrintWriter out = res.getWriter();
   String text = "Tekst o 7 krasnoludkach i innych takich.";

   out.println("Dane pobrane o " + new Date());
   for (int i = 0; i &lt; 10; i++)
    out.println(text);

   out.close();
 }
}</pre>
<p>Metoda doGet zwraca jako odpowiedź tekst zawierający datę jej wygenerowania. Jeśli w żądaniu ajaxowym podamy parametr o nazwie osoba to zostanie on również dodany do odpowiedzi.<br />
Dobrze. Mamy już potrzebny serwlet wiec teraz dodamy o nim wpis do deskryptora serwera czyli pliku web.xml.</p>
<pre class="prettyprint">
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4"&gt;

 &lt;display-name&gt;J2ee.pl - Prototype&lt;/display-name&gt;

 &lt;servlet&gt;
  &lt;servlet-name&gt;dataServlet&lt;/servlet-name&gt;
  &lt;servlet-class&gt;pl.j2ee.prototypejs.servlet.DataServlet&lt;/servlet-class&gt;
 &lt;/servlet&gt;

 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;dataServlet&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/data&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;

 &lt;welcome-file-list&gt;
  &lt;welcome-file&gt;index.html&lt;/welcome-file&gt;
 &lt;/welcome-file-list&gt;
&lt;/web-app&gt;</pre>
<p>Jak widzimy serwlet DataServlet będzie obsługiwał żądania, które zostaną dopasowane do wzorca /data.<br />
I jeszcze strona html. Zawiera ona kilka sekcji, w których będziemy umieszczać dane przychodzące z serwera w kilku następnych przykładach oraz funkcję javascrpit wysyłającą ajaxowe żądanie.</p>
<pre class="prettyprint">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;
&lt;head&gt;
 &lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"/&gt;
 &lt;script src="js/prototype.js" type="text/javascript"&gt;&lt;/script&gt;
 &lt;style type="text/css"&gt;
  .section {
   margin: 5px 5px 5px 5px;
   padding: 2px 2px 2px 2px;
   width: 500px;
  }
  .data {
   background-color: #60A7F2;
   border: 2px solid #388EF2;
  }
  .errors {
   background-color: #FF9966;
   border: 2px solid #ED3D16;
  }
 &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;script type="text/javascript"&gt;
  function testAjax() {
   new Ajax.Request('/prototypejs/data',
    {
     method:'get',
     onSuccess: function(transport) {
      $('news').innerHTML = transport.responseText || "brak danych";
     },
     onFailure: function() {
      alert('Wystąpił błąd podczas pobierania danych...')
     }
    });
  }
 &lt;/script&gt;
 &lt;button onclick="testAjax()"&gt;Pobież dane&lt;/button&gt;
 &lt;div class="section data"&gt;Dane pobrane z serwera: &lt;span id="news"&gt;&lt;/span&gt;&lt;/div&gt;
 &lt;div id="errors" class="section errors"&gt;&lt;/div&gt;
 &lt;div id="newsContent" class="section data"&gt;Dane pobrane z serwera:&lt;/div&gt;
 &lt;div class="section data"&gt;Dane w tej sekcji będą uaktualniane okresowo:
  &lt;span id="dynamicContent"&gt;&lt;/span&gt;
 &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>W <a href="http://prototypejs.org" target="_blank">Prototype</a> funkcjonalność związana z technologią Ajax została zawarta w obiekcie Ajax oraz kilku funkcjach serializujacych dane. Żądania ajaxowe wysyłamy w bardzo prosty sposób. Wystarczy utworzyć obiekt Ajax.Request, wskazać url, do którego ma się odnieść oraz podać kilka parametrów związanych z żądaniem i jego obsługą. Konstruktor obiektu Ajax.Request przyjmuje dwa parametry. Pierwszym z nich jest url, a drugim hashmapa z parametrami. W naszym przykładzie podajemy trzy parametry: typ żądania oraz funkcje obsługującą dane w przypadku poprawnej bądź niepoprawnej obsługi naszego żądania. Żądania ajaxowe są z reguły asynchroniczne, stąd metody, które przekazujemy do ich obsługi są traktowane jako funkcje callback. Oznacza to iż są one wywoływane gdy otrzymamy odpowiedni status obsługi naszego żądania ajaxowego. <a href="http://prototypejs.org/api/ajax/options" target="_blank">Tutaj</a> znajdziemy spis parametrów, które możemy podać w żądaniu wraz z ich domyślnymi wartościami.<br />
Dla osób, którym powyższy zapis wydaje się nieco nieprzejrzysty mam dobrą wiadomość. Otóż możemy przepisać powyższy kod javascript do nieco ładniejszej postaci. Drugi argument możemy zapisać w notacji JSON. Teraz nasza funkcja wygląda nieco przejrzyściej.</p>
<pre class="prettyprint">
&lt;script type="text/javascript"&gt;
 function putText(transport) {
  $('news').innerHTML = transport.responseText || "brak danych";
 }
 function showErrorMsg() {
  alert('Wystąpił błąd podczas pobierania danych...');
 }

 function testAjax() {
  var url      = "/prototypejs/data";
  var params = new Object();

  params.method = "get";
  params.parameters = "osoba=Leszek Peszek";
  params.onComplete = putText;
  params.onFailure = showErrorMsg;

  new Ajax.Request(url, params);
 }
&lt;/script&gt;</pre>
<p>Ajax często wykorzystywany jest w ten sposób, że pobiera część strony www z serwera i bez modyfikacji wstawia ją do odpowiedniej sekcji strony. <a href="http://prototypejs.org" target="_blank">Prototype</a> wychodzi na przeciw tym praktykom udostępniając obiekt Ajax.Updater. Działa on na podobnej zasadzie co Ajax.Request, tyle że umożliwia stosowanie łatwiejszego zapisu. W konstruktorze obiektu podajemy tym razem trzy argumenty. Są to: identyfikator sekcji, do której wstawiona zostanie pobrana z serwera treść, url oraz parametry żądania. Oto malutki przykład.</p>
<pre class="prettyprint">
&lt;script type="text/javascript"&gt;
 function testAjax() {
  new Ajax.Updater('newsContent', '/prototypejs/data',
   { method: 'post'}
  );
 }
&lt;/script&gt;</pre>
<p>Nie musimy podawać żadnych funkcji obsługujących to żądanie. Jeśli otrzymamy status żądania świadczący o udanym pobraniu danych z serwera to <a href="http://prototypejs.org" target="_blank">Prototype</a> sam wstawi pobraną treść do wskazanej sekcji. Bardzo zgrabnie obsługiwany jest przypadek wystąpienia jakiegokolwiek błędu. Otóż jeśli zastosujemy zapis jak powyżej to treść błędu nie zostanie wstawiona do podanej sekcji. Po prostu stracimy informację o zaistniałej błędnej sytuacji. Takie podejście jest najczęściej stosowane gdyż zwykle nie chcemy pokazać osobie przeglądającej naszą stronę brzydkiego komunikatu o błędzie. Niemniej jednak jeśli po stronie serwera przechwytujemy wszelkie błędy i opakowujemy je w ładne komunikaty to możemy je również wyświetlić na stronie www.</p>
<pre class="prettyprint">
&lt;script type="text/javascript"&gt;
 function testAjax() {
  new Ajax.Updater({ success:'newsContent', failure:'errors' },
   '/prototypejs/data',
   { method: 'post', insertion: Insertion.Top }
  );
 }
&lt;/script&gt;</pre>
<p>Tym razem pierwszy argument przekazywany do konstruktora obiektu Ajax.Updater jest hashmapą, w której podajemy dwa identyfikatory sekcji. Jeśli nie wystąpi żaden błąd to pobrane dane zostaną wstawione do sekcji &#8216;newsContent&#8217;, natomiast jeśli pojawi się jakikolwiek błąd to jego treść znajdzie się w sekcji &#8216;errors&#8217;.<br />
W drugim przykładzie pojawiła się jeszcze jedna nowość. Podajemy dodatkowy parametr żądania &#8216;insertion: Insertion.Top&#8217;, który informuje obiekt Ajax.Updater by wstawił pobraną treść &#8220;przed&#8221; tą, która znajduje się już w danej sekcji. Więc jeśli pojawi się jakiś błąd związany z żądaniem ajaxowym to informacja o błędzie zostanie wstawiona do sekcji gdzie, być może, mamy już inne komunikaty o błędach, na wyeksponowanej, pierwszej pozycji.</p>
<p>Inne dość często wykorzystywane zastosowanie technologii Ajax polega na okresowym uaktualnianiu części strony www, np. co minutę sekcja z najświeższymi informacjami ze świata Javy jest aktualizowana. Również tym razem <a href="http://prototypejs.org" target="_blank">Prototype</a> dostarcza nam gotowy obiekt, który przy naszym minimalnym wkładzie zajmie się tym trudnym zadaniem ;). obiekt nazywa się Ajax.PeriodicalUpdater i stosuje się go podobnie do tych wcześniej opisanych.</p>
<pre class="prettyprint">
&lt;script type="text/javascript"&gt;
 function testAjax() {
  new Ajax.PeriodicalUpdater('dynamicContent',
   '/prototypejs/data',
   {method: 'get', frequency: 3, decay: 2}
  );
 }
&lt;/script&gt;</pre>
<p>Pierwszym argumentem jest identyfikator sekcji, do której dane będą wstawiane. Dalej mamy url oraz paczkę z parametrami żądania. W przykładzie podajemy parametr &#8216;frequency&#8217;, który określa jak często żądanie będzie przesyłane. W tym przypadku są to trzy sekundy. Kolejny parametr &#8216;decay&#8217; mówi ile razy obiekt Ajax.PeriodicalUpdater ma zwiększyć parametr &#8216;frequency&#8217; jeśli treść żądania, którą właśnie otrzymaliśmy jest identyczna z tą wcześniej otrzymaną. Pozwala to zmniejszyć obciążenie serwera na serwisach gdzie niusy nie pojawiają się zbyt często :).</p>
<p><a href="http://prototypejs.org" target="_blank">Prototype</a> zostało opracowane po to by ułatwić programowanie w JavaScript. Jego twórca miał kilka ciekawych pomysłów, które zostały udanie zaimplementowane. Zostało to zauważone również przez twórców innych bibliotek JavaScript (jak choćby <a href="http://prototype-window.xilinus.com" target="_blank">Prototype Window</a>), które wykorzystują <a href="http://prototypejs.org" target="_blank">Prototype</a> jako swój szkielet, swoją bazę. W prosty sposób uzyskujemy dostęp do szerokiej gammy funkcji, którą zresztą możemy sami rozszerzać o nasze własne. Poprzez wsparcie dla technologii Ajax jej użycie staje się dziecinnie proste i bardzo wygodne. Za jedyną słabszą stronę <a href="http://prototypejs.org" target="_blank">Prototype</a> można uznać jej dokumentację, która w niektórych miejscach nie jest zbyt obszerna. Niemniej jednak ubytek ten będzie nam zrekompensowany przez mającą się ukazać w najbliższym czasie książkę pt. &#8216;<a href="http://pragmaticprogrammer.com/titles/cppsu/index.html" target="_blank">Prototype and script.aculo.us&#8217;</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2007/08/07/prototype/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
