<?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; direct web remoting</title>
	<atom:link href="http://j2ee.pl/tag/direct-web-remoting/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>
	</channel>
</rss>
