<?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; Inne biblioteki</title>
	<atom:link href="http://j2ee.pl/category/inne-biblioteki/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>Exadel Flamingo</title>
		<link>http://j2ee.pl/2008/12/12/exadel-flamingo/</link>
		<comments>http://j2ee.pl/2008/12/12/exadel-flamingo/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 13:00:05 +0000</pubDate>
		<dc:creator>Dariusz Sot</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Technologie]]></category>
		<category><![CDATA[Adobe Flex]]></category>
		<category><![CDATA[Flamingo]]></category>
		<category><![CDATA[JavaFX]]></category>
		<category><![CDATA[RIA]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=471</guid>
		<description><![CDATA[Ostatnimi czasy można zauważyć rosnącą popularność aplikacji typu RIA. Istnieje wiele technologii służących do tworzenia takich aplikacji – najważniejsze z nich to Adobe Flex oraz JavaFX. O ile pierwsza z nich ma już swoją pozycję na runku, to o drugiej jeszcze nie słychać zbyt dużo w kontekście portalowych stron WWW.
Jak wiadomo, w przypadku nowych technologii [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://exadel.com/web/portal/flamingo" target="_blank"><img src="http://j2ee.pl/wp-content/uploads/2008/12/images.jpeg" border="0" alt="" title="Exadel" width="90" height="48" class="alignleft size-medium wp-image-472" style="margin: 5px; float: left;" /></a>Ostatnimi czasy można zauważyć rosnącą popularność aplikacji typu RIA. Istnieje wiele technologii służących do tworzenia takich aplikacji – najważniejsze z nich to Adobe Flex oraz JavaFX. O ile pierwsza z nich ma już swoją pozycję na runku, to o drugiej jeszcze nie słychać zbyt dużo w kontekście portalowych stron WWW.</p>
<p>Jak wiadomo, w przypadku nowych technologii zawsze najtrudniejszy jest ten ‘pierwszy krok’:</p>
<ul>
<li>jak skonfigurować środowisko?</li>
<li>jak stworzyć szkielet projektu?</li>
<li>jak to wszystko spiąć żeby dobrze działało?</li>
</ul>
<p>Na wszystkie powyższe pytania i problemy jest jednak prosta i szybka odpowiedź: <strong>Exadel Flamingo</strong>.<span id="more-471"></span></p>
<p><strong>Na problemy – Flamingo</strong></p>
<p>Flamingo to zestaw bibliotek i komend służących jako pomoc w wygenerowaniu i konfiguracji szkieletu aplikacji typu RIA. Obsługa generatora Flamingo jest bardzo prosta. Polega na odpowiedzi na kilka pytań zadawanych w formie wizarda – na ich podstawie jest generowany odpowiedni projekt. Flamingo bazuje na Mavenie i każdy wygenerowany projekt jest w tej konwencji. Dzięki temu bardzo prosto można odnaleźć się w projekcie – oczywiście dla ludzi znających tą konwencję, ale chyba w dzisiejszych czasach znajomość Mavena jest raczej powszechna ;-)</p>
<p>Wygenerowany kod szkieletu posiada pełne wsparcie i jest skonfigurowany do pracy pomiędzy Flexem lub JavąFX a JBoss Seamem lub Springiem. Każdy znajdzie więc coś dla siebie, gdyż Seam to raczej podstawowa sprawa jeżeli chodzi o aplikacje EJB3, a Spring – to już klasyka. Wybór jest więc zatem ograniczony do najbardziej popularnych aktualnie technologii i oznacza, że  w ogromnej większości przypadków takiego ograniczenia nie trzeba sztucznie obchodzić.</p>
<p>Wygenerowany szkielet jest naprawdę kompletny, Flamingo zajmuje się tutaj wszystkim:</p>
<ul>
<li>generuje odpowiednie klasy (klasę) odpowiedzialne za (bardzo prosty) model danych</li>
<li>generuje warstwy aplikacji, włączając w to serwisy oraz DAO oraz konfiguruje całą komunikację, począwszy od interfejsu użytkownika a kończąc na bazie danych</li>
<li>generuje strukturę aplikacji webowej, z wszystkimi wymaganymi plikami</li>
<li>generuje podstronę logowania, przez co mechanizm autoryzacji jest już gotowy do rozszerzenia</li>
<li>generuje szkielet do testów oraz podstawowy test</li>
</ul>
<p><strong>Jak to działa?</strong></p>
<p>Odpalmy zatem Flamingo – robimy to poprzez uruchomienie konsoli i wpisaniu ‘<em>flamingo create project</em>’ (binaria flamingo muszą być dostępne). Famingo zacznie ściągać wszystkie biblioteki, o ile ich już nie posiadamy w naszym lokalnym repozytorium Mavenowym i następnie uruchamia konsolowego wizarda. Nie będę opisywał kolejnych pytań, bo są raczej oczywiste – zamieszczę tylko zrzut z mojej konsoli:</p>
<pre>
Please choose the type of application to generate (flamingo-seam, flamingo-spring):[flamingo-spring]
flamingo-spring
Please enter the location in which your new application will be created (i.e. c:/java/development):
S:/_Projects/Java/J2EE
Enter the project name (e.g. myproject):
Flex
Please enter the root package name for your project (e.g. com.mydomain.myproject): [pl.j2ee.flamingo]
pl.j2ee.flex
Will this project have Flex or JavaFX user interface? (flex, javafx): [flex]
flex
Will this project use Hessian or AMF protocol? (amf, hessian): [amf]
amf
What kind of database are you using? (hsql, mysql, oracle, postgres, mssql, db2, sybase, none): [hsql]
hsql
Enter the JDBC URL for your database (e.g. jdbc:hsqldb:.): [jdbc:hsqldb:flex]
jdbc:hsqldb:flex
Enter database username: [sa]
sa
Enter database password: []

Do you want to update the database schema each time you deploy? (y, n): [y]
y
Enter the entity class name (Flex source files to view and modify entities will be generated as well):
Department
-------------------------------------------------------------------------------------
    G e n e r a t i n g   A n d r o M D A   P o w e r e d   A p p l i c a t i o n
-------------------------------------------------------------------------------------
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flamingoproject.properties'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flex/pom.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flex/src/main/flex/pl/j2ee/flex/view/DepartmentManager.mxml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flex/src/main/flex/pl/j2ee/flex/vo/Department.as'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flex/src/main/flex/main.mxml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/flex/src/main/resources/services-config.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/pom.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/readme.txt'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/pom.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/Department.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/DepartmentDAO.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/DepartmentDAOImpl.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/service/DepartmentService.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/service/ILoginService.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/java/pl/j2ee/flex/service/LoginService.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/resources/applicationContext-test.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/resources/applicationContext.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/resources/hibernate.cfg.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/webapp/index.html'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/main/webapp/WEB-INF/web.xml'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/test/java/pl/j2ee/flex/dao/DepartmentDAOTest.java'
    Output: 'file:/S:/_Projects/Java/J2EE/Flex/web/src/test/java/pl/j2ee/flex/DataSourceTestCase.java'
-------------------------------------------------------------------------------------
    New application generated to --> 'file:/S:/_Projects/Java/J2EE/Flex/'
-------------------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 minute 24 seconds
[INFO] Finished at: Fri Nov 28 14:36:08 CET 2008
[INFO] Final Memory: 10M/20M
[INFO] --------------------------------------------------------------
</pre>
<p>No, i tyle :-) Proste, prawda? </p>
<p><strong>Wygenerowany kod</strong></p>
<p>Przechodzimy teraz do folderu gdzie wygenerował się nasz szkielet. Po wejściu do niego możemy zbudować cały projekt – poprzez ‘<em>mvn clean install</em>’. Po chwili – naszym oczom ukazuje się jakże pożądany komunikat: „<em>Build Successful</em>” (na konsoli widać także przeprowadzony test zapisu naszej klasy modelowej – także mechanizm testowania działa poprawnie). Świetnie, mamy coś co działa i się buduje, ale co tak naprawdę wygenerowaliśmy? Spróbujmy zatem zdeplyować naszą aplikację. Po wrzuceniu wara do Tomcata niestety okazuje się, że nie ma róży bez ognia – konsola krzyczy że nie mamy wymaganej biblioteki do <em>HSQL</em>. Przechodzimy zatem do odpowiedniego pliku POM (w web) i po jego otworzeniu – widzimy co jest nie tak. Usuwamy zbędną linijkę &#8216;&lt;scope&gt;test&lt;/scope&gt;’ dla dependency <em>HSQLDB</em>, zapisujemy zmiany i ponownie budujemy projekt (‘<em>mvn clean install</em>’). Ponownie wrzucamy wara do tomcata i już – aplikacja wstała poprawnie!</p>
<p>Nie pozostaje nic innego jak teraz przejść do przeglądarki i zobaczyć nasze dzieło. Po uruchomieniu odpowiedniego adresu naszym oczom ukazuje się piękna strona we flashu, która żąda od nas danych do zalogowania. Dane te nie są istotne, to i tak jest stub – metoda do autoryzacji zawsze zwraca ‘<em>true</em>’ ;-)  Po poprawnym zalogowaniu przechodzimy do jedynej dostępnej strony – listy obiektów domenowych (w moim przypadku ‘<em>Department</em>’). Mamy tutaj możliwość przeglądania wszystkich wpisów oraz funkcjonalności dodania nowego, edycji oraz usunięcia istniejącego. Sama lista oferuje jeszcze możliwość sortowania wyników. Ostatnią funkcjonalnością jest jeszcze oferowana przez guzik ‘<em>Logout</em>’ opcja wylogowania się z systemu. I to by było na tyle!</p>
<p><strong>To działa!</strong></p>
<p>Jak widać z powyższego tekstu – wystarczy ściągnąć Flamingo, rozpakować, odpalić wizarda i mamy już gotowy szkielet aplikacji <em>RIA</em>. Należy przy tym podkreślić, że w całym procesie od momentu pomysłu o szkielecie aplikacji flexowej do jej uruchomienia ani razu nie potrzebowalismy uruchomić żadnego IDE. <strong>Niesamowite!</strong> Co więcej, wygenerowany szkielet ten jest kompletny i tak naprawdę jest już w pełni działającą aplikacją o minimalnej funkcjonalności, przedstawiającą funkcjonalności bardziej na zasadzie ‘how to’. Wystarczy zaimplementować poprawne logowanie oraz rozszerzyć model dziedziny – i mamy w bardzo przystępny sposób pokazaną drogę w świat <em>RIA</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/12/12/exadel-flamingo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google Maps API i Java</title>
		<link>http://j2ee.pl/2008/10/03/google-maps-api-i-java/</link>
		<comments>http://j2ee.pl/2008/10/03/google-maps-api-i-java/#comments</comments>
		<pubDate>Fri, 03 Oct 2008 09:20:51 +0000</pubDate>
		<dc:creator>Rafał Jaskółka</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Google Maps API]]></category>
		<category><![CDATA[wicket]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=357</guid>
		<description><![CDATA[ 
Prawdopodobnie  tego bloga nie czyta nikt, kto nigdy nie używał Google Maps (beta ;-) ). Google od dłuższego czasu udostępnia API do Google Maps. Jako, że aplikacja jest napisana w języku javascript, tak też jest w wypadku API. Sama aplikacja jest w trakcie permanentnego rozwoju, wraz z nim zmienia się też interfejs programistyczny.

Konfiguracja [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.google.com/apis/maps/"> <img class="alignleft size-medium wp-image-409" title="maps_logo_small_blue" src="http://j2ee.pl/wp-content/uploads/2008/10/maps_logo_small_blue.png" alt="" width="150" height="55" /></a><a href="http://wicketstuff.org/confluence/display/STUFFWIKI/wicket-contrib-gmap2"><img class="alignleft size-medium wp-image-408" title="logo_wicket_stuff" src="http://j2ee.pl/wp-content/uploads/2008/10/logo_wicket_stuff.png" alt="" width="200" height="51" /></a><br />
Prawdopodobnie  tego bloga nie czyta nikt, kto nigdy nie używał<a href="http://mapy.google.pl/"> Google Maps (beta ;-) )</a>. Google od dłuższego czasu udostępnia <a href="http://code.google.com/apis/maps/">API do Google Maps</a>. Jako, że aplikacja jest napisana w języku javascript, tak też jest w wypadku API. Sama aplikacja jest w trakcie permanentnego rozwoju, wraz z nim zmienia się też interfejs programistyczny.<br />
<span id="more-357"></span></p>
<p><strong>Konfiguracja i narzędzia</strong><br />
Jak sama nazwa wskazuje ten blog skupia się na szeroko pojętej korporacyjnej Javie, więc pokażę kilka zastosowań API w aplikacjach w tym języku. Oczywiście można sobie wyobrazić aplikację, która będzie wywoływać metody z <em>Google Maps API</em> bezpośrednio, ale żeby zaoszczędzić sobie pracy można wykorzystać np. otwarte rozszerzenie Apache Wicket  <a href="http://wicketstuff.org/confluence/display/STUFFWIKI/wicket-contrib-gmap2">wicket-contrib-gmap2</a>.</p>
<p>Biblioteka ta jest dostępna w ramach  <a href="http://wicketstuff.org/confluence/display/STUFFWEB/Home">Wicket Stuff</a> – zbioru bibliotek  nie należących do głównego projektu <a href="http://wicket.apache.org/">Apache Wicket</a>, lecz silnie z tym frameworkiem powiązanych. W związku z czysto społecznościowym modelem rozwoju, i zmiennością samego <em>Google Maps API</em>, rozszerzenie w tej chwili nie ma w tej chwili stabilnego wydania. Jedną z ujemnych stron takiego modelu rozwoju jest konieczność samodzielnego zbudowania projektu ze źródeł, lub skorzystania z migawek aktualnego repozytorium.</p>
<p>Posłużę się podobnym schematem projektu jak <a href="http://j2ee.pl/2008/07/07/apache-wicket-troche-inny-framework/">w poprzednim wpisie na temat Wicketa </a>choć tym razem z wykorzystaniem migawki rozwojowej gałęzi 1.4. W porównaniu do wersji 1.3.x zmiany związane są z pełniejszym wsparciem dla cech języka wprowadzonych w wersji 5 Javy. Pliki jar wicketa jak i projektu  <em>wicket-contrib-gmap2</em> są zawarte<a href="http://wicketstuff.org/maven/repository/"> w repozytorium migawek Wicket Stuff</a>, więc odpada konieczność samodzielnego budowania tych zależności.</p>
<p><strong>Kod  żródłowy</strong><br />
Całość projektu do zbudowania z użyciem mavena jest dostępna <a href="http://j2ee.pl/wp-content/uploads/2008/10/j2ee-wicket-gmap2.zip">w tej paczce</a>.</p>
<p><strong>Klucz</strong><br />
<em>Google Maps API</em> wymaga wygenerowania klucza dla URL pod którym aplikacja będzie widoczna. Klucz można <a href="http://code.google.com/apis/maps/signup.html">wygenerować na tej stronie</a>, wymagane jest konto Google i zaakceptowanie warunków użycia API. Klucz będzie działał dla każdego URL (<em>katalogu</em>, domeny) rozpoczynającego się od ciągu użytego do jego wygenerowania. Dodatkowe informacje są zawarte na stronie na której klucz jest generowany. Pewnym ograniczeniem jest konieczność publicznego udostępnienia aplikacji korzystającej z API, chyba że zdecydujemy się na płatną wersję serwisu – <em>Google Maps API Premier</em>.</p>
<p>Klucz API (definiowany w<em> GmapApplication.java</em>) w przykładowym projekcie wygenerowany został dla http://localhost więc nie będzie konieczna jego zmiana dla aplikacji działającej lokalnie. API używane lokalnie traktuje klucz  bardziej liberalnie, np. obsługuje aplikację uruchomioną na innym niż zadeklarowany porcie.</p>
<p>Przyjrzyjmy się klasie <em>GmapApplication.java</em> wywoływanej przy starcie aplikacji. W polu <em>googleMapsApiKey</em> zawarty jest klucz używany w całej aplikacji.</p>
<p>Linia<code>getMarkupSettings().setStripWicketTags(true);</code> służy do wycięcia z wygenerowanego na podstawie szablonów HTML tagów specyficznych dla <em>wicketa</em>. Dodanie jej jest konieczne, by aplikacja w trybie <em>DEVELOPMENT</em> działała w Firefox 3. W klasie tej jest też konfigurowana domyślna strona – <em>MapPage</em>.</p>
<p><strong>Współrzędne</strong><br />
Google Maps jako że jest szczególną formą mapy, ma też pewne wady map w ogólności. Podstawową jest próba odwzorowania powierzchni geoidy, którą w przybliżeniu jest ziemia w postaci płaskiej powierzchni. Twórcy map rozwiązują ten problem stosując różnego typu odwzorowania – w przypadku Google Maps jest to uproszczone <a href="http://pl.wikipedia.org/wiki/Odwzorowanie_walcowe_r%C3%B3wnok%C4%85tne">odwzorowanie  Mercatora</a>. Odwzorowanie to bazuje na przekształceniu powierzchni geoidy na walec styczny do równika, i rozwinięciu powierzchni walca. Google maps dodatkowo traktuje powierzchnię ziemi jako idealną sferę, co upraszcza obliczenia na tyle by mogły one być prowadzone w API w języku javascript. Takie podejście obniża dokładność, ale głównym zadaniem Google Maps jest prezentacja ładnych, a nie wiernych  map :-).</p>
<p><strong>Kodowanie geograficzne – geocoding</strong><br />
Współrzędne na mapie są określone przez długość i szerokość geograficzną.Na podstawie takich współrzędnych działa też mechanika Google Maps. Ale w życiu codziennym takimi współrzędnymi posługujemy się bardzo rzadko, za <em>współrzędne</em> służy nam adres. Możliwość dość dokładnego odnajdywania miejsc z pomocą adresu w formie w jakiej używamy go na co dzień jest jedną z przyczyn sukcesu <em>Google Maps</em> i podobnych usług. Google udostępnił programistom  taką funkcjonalność, zarówno jako konfigurowalny serwis HTTP, jak i interfejs w ramach samego API.</p>
<p>Pierwsza opcja jest o tyle ciekawa że można jej użyć na serwerze ( z ograniczeniami wynikającymi z licencji &#8211;  ta funkcjonalność także wymaga klucza) do reprezentacji danych powiązanych geograficznie gdy cała informacja na temat lokalizacji ogranicza się do danych adresowych. Niestety dane geograficzne na temat terytorium Polski są dość gruboziarniste, czasami działają z dokładnością do ulicy, dla mniejszych miejscowości są jeszcze mniej dokładne.</p>
<p>W opisywanym projekcie wykorzystałem <em>geocoding</em> po stronie serwera – Kod jest zawarty w klasie <em>GeoLocationClient</em>, wykorzystuje klasę z <em>gmap2-wicket-contrib</em> ( <em>Geocoder</em> ). Ta klasa pobiera informacje w najprostrzej fomie – długości i szerokości geograficznej dla danego adresu, choć serwis ma większe możliwości, pozwala na wyciągnięcie dodatkowych informacji na temat lokalizacji w postaci pliku w formacie <em>KML</em> opartym na <em>XML</em>. Ten format ma szersze zastosowanie, w samym Google MAPs, ale też np. w aplikacji Google Earth.</p>
<p><strong>Działanie przykładowego projektu</strong><br />
Strona po załadowaniu prezentuje mapę z polem tekstowym do wpisywania adresu. Na samej mapie wyświetlają się <em>pinezki</em> generowane dynamicznie. Pinezki są ładowane w momencie przesunięcia mapy i zmiany skali. Po kliknięciu w pinezkę, można zobaczyć współrzędne dla których została przyczepiona.<br />
<a href="http://j2ee.pl/wp-content/uploads/2008/10/mapa.png"><img class="alignnone size-full wp-image-421" title="mapa" src="http://j2ee.pl/wp-content/uploads/2008/10/mapa.png" alt="" width="499" height="406" /></a><br />
Jedyną stroną prezentowanego projektu jest MapPage, zawiera mapę z wyszukiwarką bazującą na wspomnianym wyżej serwisie:</p>
<pre class="prettyprint">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;

&lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
    xmlns:wicket="http://wicket.sourceforge.net/"&gt;
&lt;head&gt;
&lt;meta http-equiv="content-type" content="text/html; charset=UTF-8" /&gt;
&lt;title&gt;Wicket Examples - GMap Panel&lt;/title&gt;
&lt;style type="text/css"&gt;

v\: * {
	behavior: url(#default#VML);
}
&lt;/style&gt;
&lt;link rel="stylesheet" type="text/css" href="style.css" /&gt;
&lt;link wicket:id="infoWindowCss" rel="Stylesheet" type="text/css" href="infoWindow.css" /&gt;
&lt;/head&gt;

&lt;body&gt;

&lt;div wicket:id="locationPanel"&gt;&lt;/div&gt;
&lt;center&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;
        &lt;div wicket:id="topPanel" style="width: 800px; height: 600px;"&gt;topPanel&lt;/div&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Jak widać szablon strony jest bardzo prosty, bardziej interesujące rzeczy dzieją się w kodzie źródłowym i javascripcie.</p>
<pre class="prettyprint">package pl.j2ee.wicket.gmap2.mappage;

import org.apache.wicket.PageParameters;
....
public class MapPage extends WebPage {

    private static final String ADDRESS = "Polska, Krak\u00F3w, Bociana 22";
    private static final int MARKERS_ON_X = 4;
    private static final int MARKERS_ON_Y = 4;

    private GMap2 map;

    public MapPage(final PageParameters parameters) {
        add(new StyleSheetReference("infoWindowCss", InfoWindowPanel.class, "infoWindow.css"));
        map = new GMap2("topPanel", GmapApplication.get().getGoogleMapsApiKey());
        add(map);
        setMapBaseSettings(map);
        map.add(new ClickListener() {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onClick(AjaxRequestTarget target, GLatLng latLng, GOverlay overlay) {
                if (latLng != null) {
                    addMarker(map, latLng);
                }
            }
        });
        map.add(new DragEndListener() {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onDragEnd(AjaxRequestTarget target) {
                regenerateMarkers();
            }
        });

        map.add(new ZoomEndListener() {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onZoomEnd(AjaxRequestTarget target, int oldLevel, int newLevel) {
                regenerateMarkers();
            }
        });

        map.add(new LoadListener() {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onLoad(AjaxRequestTarget target) {
                regenerateMarkers();
            }
        });
        add(new LocationPanel("locationPanel", new CompoundPropertyModel
(new AddressBean()), map));
    }

    private void setMapBaseSettings(final GMap2 map) {
        map.setDoubleClickZoomEnabled(true);
        map.setMapType(GMapType.G_HYBRID_MAP);
        map.setScrollWheelZoomEnabled(true);
        map.setDraggingEnabled(true);
        map.setMapType(GMapType.G_NORMAL_MAP);
        map.addControl(GControl.GLargeMapControl);
        map.addControl(GControl.GMapTypeControl);
        map.addControl(GControl.GScaleControl);
        String address = getStartAddress();
        GLatLng latLng = getAddressLocation(address);
        if (latLng != null) {
            map.setCenter(latLng);
        }
    }

    private GLatLng getAddressLocation(String address) {
        GeoLocatorClient geoLocatorClient = GmapApplication.get().getGeoLocatorClient();
        GLatLng latLng = geoLocatorClient.getAddressLocation(address);
        return latLng;
    }

    private String getStartAddress() {
        String address = new ResourceModel("address", ADDRESS).getObject();
        return address;
    }

    public GMarker getNewMarker(GLatLng location) {
        GMarkerOptions options = new GMarkerOptions(getPositionDescription(location));
        options.autoPan(true);
        options.bouncy(true);
        options.clickable(true);
        final GMarker result = new GMarker(location, options);

        return result;
    }

    private String getPositionDescription(GLatLng location) {
        return " latitude: " + location.getLat() + " longitude" + location.getLng();
    }

    public void regenerateMarkers() {
        map.removeAllOverlays();

        GLatLngBounds bounds = map.getBounds();
        GLatLng sw = bounds.getSW();
        GLatLng ne = bounds.getNE();
        double latSize = ne.getLat() - sw.getLat();
        double lngSize = ne.getLng() - sw.getLng();
        double latDelta = latSize / (MARKERS_ON_Y - 1);
        double lngDelta = lngSize / (MARKERS_ON_X - 1);
        for (double x = sw.getLng(); x &lt; = ne.getLng(); x += lngDelta) {
            for (double y = sw.getLat(); y &lt;= ne.getLat(); y += latDelta) {
                GLatLng latLng = new GLatLng(y, x);
                addMarker(map, latLng);
            }

        }
    }

    private GMarker addMarker(final GMap2 map, final GLatLng latLng) {
        final GMarker result = getNewMarker(latLng);
        map.addOverlay(result);
        result.addListener(GEvent.click, new GEventHandler() {

            private static final long serialVersionUID = 1L;

            @Override
            public void onEvent(AjaxRequestTarget target) {
                GInfoWindow infoWindow = map.getInfoWindow();
                infoWindow.open(result, new InfoWindowPanel("markerPanel", new CompoundPropertyModel(latLng)));
            }
        });
        return result;
    }
}</pre>
<p><code>setMapBaseSettings</code> służy do inicjalizacji mapy, dodaje podstawowe kontrolki. W tej metodzie również ustawiana jest też lokalizacja w której mapa startuje, na podstawie adresu.</p>
<pre class="prettyprint">        GLatLng latLng = getAddressLocation(address);
        if (latLng != null) {
            map.setCenter(latLng);
        }</pre>
<p>Metoda <code>regenerateMarkers()</code> tworzy zestaw <em>pinezek</em> i dodaje je do mapy. Pinezki wcześniej dodane na mapie są usuwane. Można sobie wyobrazić, że <em>pinezki</em> pobierane z bazy danych na podstawie aktualnej skali i fragmentu mapy który jest właśnie wyświetlany. Ta metoda jest wywoływana z metod wywoływanych w wyniku zapytań <em>AJAX</em> z przeglądarki <code>DragEndListener,ZoomEndListener,LoadListener</code>.<br />
Te metody są wywoływane odpowiednio po przesunięciu, zmianie skali i załadowaniu mapy.</p>
<p>Do pinezek podpięte jest zdarzenie AJAXowe odpalone po kliknięciu:</p>
<pre class="prettyprint">result.addListener(GEvent.click, new GEventHandler() {
            private static final long serialVersionUID = 1L;
            @Override
            public void onEvent(AjaxRequestTarget target) {
                GInfoWindow infoWindow = map.getInfoWindow();
                infoWindow.open(result, new InfoWindowPanel("markerPanel", new CompoundPropertyModel(latLng)));
            }
        });</pre>
<p>Okno informacyjne jest generowane przez komponent <code>InfoWindowPanel</code>, jest to najzwyklejszy w świecie komponent Wicketa :-).</p>
<p><strong>Wnioski</strong><br />
Projekt <em>gmap2-wicket-contrib</em> może być z powodzeniem punktem startowym dla rozwoju aplikacji korzystających z Google Maps i dodatkowych źródeł danych, zbyt dużych żeby przechowywać je po stronie klienta. Jednak ten projekt korzysta tylko z małej części możliwości <em>Google Maps API</em>. Skorzystanie z pozostałych wymaga dopisania nowych komponentów bezpośrednio wywołujących metody z API, te istniejące mogą być traktowane jako wzorzec i źródło pomysłów.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/10/03/google-maps-api-i-java/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaRebel</title>
		<link>http://j2ee.pl/2008/09/16/javarebel/</link>
		<comments>http://j2ee.pl/2008/09/16/javarebel/#comments</comments>
		<pubDate>Tue, 16 Sep 2008 11:49:20 +0000</pubDate>
		<dc:creator>Michał Gołacki</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JBoss]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[tomcat]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=327</guid>
		<description><![CDATA[Zdarzyło wam się kiedyś pracować z aplikacją, której deploy trwał tak długo, że można było zapomnieć co tak właściwie chcieliście uruchomić? Być może uda nam się znaleźć rozwiązanie tego problemu. JavaRebel jest pluginem do maszyny wirtualnej. Pozwala na przeładowywanie klas bez konieczności restartowania serwera.
Konfiguracja
Konfiguracja nie jest szczególnie skomplikowana, do uruchomienia potrzebny będzie sam JavaRebel oraz [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><img style="margin: 5px; float: left;" title="JavaRebellogo" src="http://j2ee.pl/wp-content/uploads/2008/09/logo.gif" alt="JavaRebel" />Zdarzyło wam się kiedyś pracować z aplikacją, której deploy trwał tak długo, że można było zapomnieć co tak właściwie chcieliście uruchomić? Być może uda nam się znaleźć rozwiązanie tego problemu. <strong>JavaRebel</strong> jest pluginem do maszyny wirtualnej. Pozwala na przeładowywanie klas bez konieczności restartowania serwera.</p>
<h2><span id="more-327"></span>Konfiguracja</h2>
<p>Konfiguracja nie jest szczególnie skomplikowana, do uruchomienia potrzebny będzie sam JavaRebel oraz serwer aplikacji. Osobiście przetestowałem serwery aplikacjyjne JBoss 4.2.3.GA oraz Tomcat 5.5.26 deployując na nich aplikację Spring&#8217;ową.</p>
<p>W obu przypadkach dodajemy linię:<br />
<code><strong>set JAVA_OPTS=-noverify -javaagent:javarebel.jar -Drebel.dirs= -%JAVA_OPTS%</strong></code><br />
uzupełniając &#8220;<strong>-Drebel.dirs=</strong>&#8221; o katalogi do których nasze IDE kompiluje klasy, oddzielając je przecinkami. W przypadku JBoss&#8217;a plikiem  do którego dodajemy linię jest <strong>run.bat</strong>, natomiast dla Tomcat&#8217;s będzie to <strong>catalina.cmd</strong>.</p>
<p>Plik javarebel.jar umieszczamy w tym samym miejscu, w którym znajdował się edytowany przez nas wcześniej, czyli katalogu bin serwera.</p>
<h2>Działanie</h2>
<p>Teraz możemy już uruchomić serwer. Naszą aplikację deployujemy na serwerze normalnie, wrzucając w odpowiednie miejsce plik war. Aplikacja zostanie uruchomiona, a JavaRebel rozpocznie monitorowanie folderu podanego wcześniej, sprawdzając czy któraś z naszych klas nie została zmieniona. Monitorowanie zmian odbywa się na podstawie znaczników czasu w klasach. Przeładowanie klasy zachowuje wszystkie istniejące instancje.</p>
<p>JavaRebel pozwala nam na wykonanie szeregu operacji na naszych klasach. Możemy zarówno dodawać jak i usuwać metody, konstruktory, pola, klasy czy adnotacje. Oczywiście pozwala nam zmieniać ciała samych metod. Niestety, ograniczeniem jest brak możliwości zamiany klasy bazowej oraz dodawanie/usuwanie interfejsów. Wydaje mi się jednak, że nie są to tak częste operacje, aby mogły stanowić problem.</p>
<h2>Spring</h2>
<p>W ostatnim czasie pojawił się plugin do JavaRebel wspierający jego działanie ze Springiem. Wspiera on rejestrowanie nowych bean&#8217;ów, kontrolerów, zarządzanie zależnościami. Wszystkie powyższe operacje mogą być wykonywane zarówno przy użyciu konfiguracji w XML&#8217;u jak i adnotacji. Co prawda narzędzie jest udostępnione jako open source, jednak wciąż należy je traktować jako wersję beta.</p>
<h2>Licencja</h2>
<p>Jest chyba największą wadą tego narzędzia, nie jest ono tanie. Licencja ciągła przy zakupie poniżej dziesięciu kosztuje 249$, a licencja roczna to koszt 99$. Cena licencji spada wraz z ich ilością.</p>
<p>Autorzy podają, że narzędzie to jest aktualnie używane przez wszystkich developerów serwisu LinkedIn. Ponadto posiadacze brązowego pasa na <a href="http://http://j2ee.pl/?p=327&#038;preview=true/NewsView.wwa?newsId=7644325">www.javablackbelt.com</a> mogą otrzymać licencję za darmo.</p>
<h2>Wrażenia</h2>
<p>Osobiście ten sposób wprowadzania zmian w aplikacji bardzo przypadł mi do gustu. Nie zauważyłem opóźnień związanych z przeładowywaniem klas. Zależności były zawsze na miejscu dzięki możliwości zachowania istniejących instancji bean&#8217;ów. Uważam, że jest to narzędzie warte rozważenia, szczególnie w przypadku aplikacji, których deployment zajmuje pokaźną ilość czasu. Jednak z tego co zdążyłem wyczytać na forum projektu, pojawiają się problemy z obsługą EJB i generalnie nie istnieje wsparcie dla jakichkolwiek frameworków (poza Springiem). Jednak istnieje możliwość darmowego przetestowania, czy narzędzie spełnia nasze oczekiwania.</p>
<p>Moim zdaniem jest to bardzo przydatne oprogramowanie przy przygotowaniu aplikacji springowych, jednak jego cena jest nieco wygórowana, biorąc pod uwagę kłopoty z EJB i brak wsparcia dla innych frameworków.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/09/16/javarebel/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Apache Wicket &#8211; trochę inny framework</title>
		<link>http://j2ee.pl/2008/07/07/apache-wicket-troche-inny-framework/</link>
		<comments>http://j2ee.pl/2008/07/07/apache-wicket-troche-inny-framework/#comments</comments>
		<pubDate>Mon, 07 Jul 2008 12:55:21 +0000</pubDate>
		<dc:creator>Rafał Jaskółka</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[wicket]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=99</guid>
		<description><![CDATA[
Apache Wicket to komponentowy framework (szkielet) dla aplikacji WWW. Możecie sobie zadać pytanie po co jeszcze jedno narzędzie, skoro mamy Struts, JSF, Spring MVC itd&#8230;
Nudne wprowadzenie
Wicket jest alternatywą dla wyżej wymienionych, stara się zmniejszyć do minimum ilość metadanych zapisanych w dziesiątkach kilobajtów złożonych plików XML i przywrócić programiście możliwość pisania w Javie.
Wicket pozwala na oddzielenie [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://j2ee.pl/wp-content/uploads/2008/07/apache_wicket_logo.png"><img class="alignleft size-medium wp-image-174" title="apache_wicket_logo" src="http://j2ee.pl/wp-content/uploads/2008/07/apache_wicket_logo.png" alt="Wicket logo" width="160" height="59" /></a><br />
<a href="http://wicket.apache.org/"><strong>Apache Wicket</strong> </a>to komponentowy framework (szkielet) dla aplikacji WWW. Możecie sobie zadać pytanie po co jeszcze jedno narzędzie, skoro mamy Struts, JSF, Spring MVC itd&#8230;<span id="more-99"></span></p>
<p><strong>Nudne wprowadzenie</strong></p>
<p>Wicket jest alternatywą dla wyżej wymienionych, stara się zmniejszyć do minimum ilość metadanych zapisanych w dziesiątkach kilobajtów złożonych plików XML i przywrócić programiście możliwość pisania w Javie.</p>
<p>Wicket pozwala na oddzielenie kodu HTML od logiki aplikacji, nie dostarcza wielu gotowych komponentów, lecz umożliwia łatwe tworzenie własnych i rozszerzanie istniejących.<br />
Framework bazuje bezpośrednio na specyfikacji serwletów, w oparciu o nią pozwala na budowę aplikacji z komponentową warstwą widoku.  Inne rozwiązania tego typu, napisane w Javie,  zazwyczaj wymuszają użycie stron JSP. Takie rozwiązanie, w wielu przypadkach wygodne, słabo nadaje się do realizowania podejścia obiektowego, czasami wymusza łamanie jego zasad. Dynamiczne generowanie HTML-a realizowane jest tam poprzez mieszanie tagów JSTL, wyrażeń EL, kodu HTML, fragmentów w Javie. W rezultacie powstaje trudny do odczytania i pielęgnacji kod podobno przypominający spaghetti :-). Moim skromnym zdaniem to porównanie jest obrazą dla tak zacnego posiłku.</p>
<p>W zależności od preferencji rezygnacja z metadanych zapisanych w plikach XML może być wadą lub zaletą. Mocnymi stronami takiego rozwiązania są lepsza podatność kodu na zmiany i możliwość korzystania ze standardowych narzędzi do refaktorowania kodu.</p>
<p>Pojedynczy komponent w Wicket składa się z dwóch części: kodu w Javie oraz pliku HTML. Komponenty mogą być dziedziczone. Dotyczy to zarówno części w HTML jak i w Javie.</p>
<p>Plik HTML korzysta ze standardowych znaczników, elementy obce są ograniczone do atrybutów <strong>wicket:id</strong> identyfikujących poszczególne komponenty oraz kilku znaczników specyficznych dla  Wicket.  Znaczniki te odpowiadają za wprowadzanie punktów w których kontrolę przejmuje kod w Javie.</p>
<p>Zachowania dynamiczne (pętle, bloki wyświetlane po spełnieniu pewnych warunków) są obsługiwane w pełni w części Javowej komponentów. Taka organizacja umożliwia podział odpowiedzialności pomiędzy programistę i projektanta WWW, pliki HTML komponentów można zmieniać za pomocą standardowych narzędzi do tego przeznaczonych.</p>
<p><strong>Do roboty!</strong></p>
<p>Kody źródłowe to tego przykładu są <a href="http://j2ee.pl/wp-content/uploads/2008/07/j2ee_wicket.zip">tu</a>.</p>
<p>Autorzy Wicket rekomendują projekt Apache Maven 2 jako narzędzie do budowania projektów opartych o ten szkielet. Rzeczywiście stworzenie szkieletu aplikacji z wykorzystaniem tego narzędzia jest łatwe i przyjemne :). Opis instalacji Mavena można znaleźć na stronie projektu.</p>
<p>Po zainstalowaniu narzędzia wydajemy polecenie:</p>
<pre class="prettyprint">mvn archetype:create
-DarchetypeGroupId=org.apache.wicket
-DarchetypeArtifactId=wicket-archetype-quickstart
-DarchetypeVersion=1.3.4
-DgroupId=pl.j2ee.wicket
-DartifactId=j2ee_wicket</pre>
<p><strong><br />
j2ee_wicket</strong> będzie nazwą naszego projektu.<br />
<strong>pl.j2ee.wicket</strong> to nazwa pakietu, w którym znajdziemy wygenerowany kod.</p>
<p>Projekt można łatwo importować do ulubionego IDE. W przypadku eclipse warto użyć wtyczki m2eclipse, w nowszych wersjach dobrze integrującej się z WTP &#8211; zestawem wtyczek dla programistów aplikacji webowych.</p>
<p>Po wykonaniu tych kroków mamy gotowy do uruchomienia prosty projekt. Pozostaje nam tylko uruchomienie klasy j2ee.pl.Start jako zwykłej aplikacji Javowej i nakierowanie przeglądarki na <a href="http://localhost:8080/j2ee_wicket">http://localhost:8080/j2ee_wicket</a>. Projekt można uruchomić również z poziomu mavena &#8211; z celem jetty:run:</p>
<pre class="prettyprint">mvn install
mvn jetty:run</pre>
<p>Jeżeli udało się wszystko dobrze skonfigurować, oczom naszym powinien ukazać się tryumfalny komunikat:<br />
<strong>Wicket Quickstart Archetype Homepage</strong><br />
<strong>If you see this message wicket is properly configured and running</strong></p>
<p>Jako że <strong>j2ee.pl.Start</strong> jest zwykłą klasą Javową debugowanie naszego projektu  będzie równie łatwe jak to jest w przypadku zwykłej aplikacji dla platformy JSE.</p>
<p>Na początek możemy sprawić by aplikacja robiła coś więcej. Zacznijmy od dodania klasy która będzie implementować prostą logikę:</p>
<pre class="prettyprint">package pl.j2ee.wicket.model;

import java.io.Serializable;

public class Suma implements Serializable {

    private static final long serialVersionUID = 6261687669280596713L;
    private Integer skladnik1;
    private Integer skladnik2;

    public Integer getSkladnik1() {
        return skladnik1;
    }

    public void setSkladnik1(Integer skladnik1) {
        this.skladnik1 = skladnik1;
    }

    public Integer getSkladnik2() {
        return skladnik2;
    }

    public void setSkladnik2(Integer skladnik2) {
        this.skladnik2 = skladnik2;
    }

    public Integer getSuma() {
        if (skladnik1 != null &amp;&amp; skladnik2 != null) {
            return new Integer(skladnik1.intValue() + skladnik2.intValue());
        }
        return null;
    }

}</pre>
<p>Dodajemy szablon <strong>SumaPage.html </strong>w pakiecie pl.j2ee.wicket</p>
<pre class="prettyprint">&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;J2ee wicket&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;span wicket:id="komunikat"&gt;miejsce na statyczny komunikat&lt;/span&gt;        &lt;form wicket:id="dodaj"&gt;
            &lt;input type="text" wicket:id="skladnik1" /&gt;
            &lt;input type="text" wicket:id="skladnik2" /&gt;
            &lt;input type="submit" wicket:id="sumuj" /&gt;
        &lt;/form&gt;
        &lt;span wicket:id="suma"&gt; wynik obliczen &lt;/span&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
<p>Wartości w atrybutach <strong>wicket:id</strong> będą identyfikatorami komponentów zdefiniowanych w <strong>SumaPage.java</strong></p>
<pre class="prettyprint">package pl.j2ee.wicket;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.BoundCompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;

import pl.j2ee.wicket.model.Suma;

/**
 * Suma
 */
public class SumaPage extends WebPage {

    private static final long serialVersionUID = 1L;
    protected Label resultLabel;
    protected Suma suma;

    public SumaPage(final PageParameters parameters) {
        add(new Label("komunikat", "Podaj dwie liczby całkowite"));
        suma = new Suma();
        Form dodajForm = new Form("dodaj", new BoundCompoundPropertyModel(suma));
        TextField skladnik1 = new TextField("skladnik1");
        dodajForm.add(skladnik1);
        TextField skladnik2 = new TextField("skladnik2");
        dodajForm.add(skladnik2);
        resultLabel = getResultLabel();
        add(resultLabel);
        dodajForm.add(getSubmitButton(dodajForm));
        add(dodajForm);
    }

    protected Label getResultLabel() {
        return new Label("suma", new PropertyModel(suma, "suma"));
    }

    protected Button getSubmitButton(Form form) {
        return new Button("sumuj", new Model("Sumuj"));
    }
}</pre>
<p>Klasa SumaPage definiuje główny komponent dla strony o tej samej nazwie. Strona jest budowana poprzez organizowanie komponentów w drzewo. Węzły w tym drzewie stanowią obiekty implementujące interfejs Component. Tylko obiekty klas implementujących interfejs MarkupContainer  (<em>kontenery</em>) mogą zawierać inne komponenty.</p>
<p>Jednym z tego typu komponentów jest formularz:</p>
<pre class="prettyprint">Form dodajForm = new Form("dodaj",new BoundCompoundPropertyModel(suma));</pre>
<p>W tej linii definiujemy formularz o identyfikatorze <em>dodaj</em> (taki sam jak w pliku <strong>HomePage.html)</strong>, oraz model powiązany z tym komponentem (<strong>BoundControlPropertyModel</strong>). Wicket zawiera wiele typów modeli, ten ma największe możliwości, pozwala komponentom składowym na dostęp do pól składowych obiektu modelu kontenera.</p>
<p>Według przyjętej konwencji jeżeli dla komponentu składowego formularza nie podano modelu, wicket próbuje powiązać ten komponent z modelem formularza interpretując identyfikator komponentu jako nazwę pola w obiekcie modelu formularza.</p>
<p>Nazwy <em>skladnik1</em> i <em>skladnik2</em> muszą być takie same jak nazwy pól w obiekcie <em>suma</em>.</p>
<pre class="prettyprint">TextField skladnik1 = new TextField("skladnik1");
dodajForm.add(skladnik1);
TextField skladnik2 = new TextField("skladnik2");
dodajForm.add(skladnik2);</pre>
<p>Musimy dodać przycisk wysyłający formularz i podpiąć formularz do strony. Klasa Model definiuje niemodyfikowalny model, w tym przypadku zawsze będzie to podany String.</p>
<pre class="prettyprint">    dodajForm.add(getSubmitButton(dodajForm));
    add(dodajForm);
    .
    .
    .
    protected Button getSubmitButton(Form form) {
        return new Button("sumuj", new Model("Sumuj"));
    }</pre>
<p>Rezerwujemy miejsce w którym zobaczymy  wynik obliczeń. PropertyModel działa podobnie do BoundControlPropertyModel lecz wiąże tylko jedno pole w beanie. Z modelu tej klasy możemy skorzystać  też w elemencie formularza.</p>
<pre class="prettyprint">    resultLabel = getResultLabel();
    add(resultLabel);
    .
    .
    .
    protected Label getResultLabel() {
        return new Label("suma", new PropertyModel(suma, "suma"));
    }</pre>
<p>By dostać się na nowo dodaną stronę, musimy dodać odnośnik na stronie głównej, odpowiednie zmiany w HomePage.java i HomePage.html:</p>
<pre class="prettyprint">add(new PageLink("sumaLink", SumaPage.class));</pre>
<pre class="prettyprint">&lt;br/&gt;&lt;a wicket:id="sumaLink"&gt;Suma&lt;/a&gt;</pre>
<p>Pozostaje uruchomienie przykładu i radość z osiągniętych wyników :-).</p>
<p><strong>Wicket i AJAX</strong></p>
<p>Wicket zawiera ciekawą obsługę technologii AJAX. Aby ją wykorzystać w naszym przykładzie wystarczy mała modyfikacja klasy SumaPage. Ale możemy też wykorzystać mechanizm dziedziczenia:</p>
<p>Dodajemy klasę SumaAjaxPage:</p>
<pre class="prettyprint">package pl.j2ee.wicket;

import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;

/**
 * Suma i ajax
 */
public class SumaAjaxPage extends SumaPage {

    protected Label getResultLabel() {

        Label resultLabel2 = super.getResultLabel();
        resultLabel2.setOutputMarkupId(true);
        return resultLabel2;
    }

    protected Button getSubmitButton(Form form) {
        return new AjaxButton("sumuj", form) {

            private static final long serialVersionUID = -7912898126800181416L;

            protected void onSubmit(AjaxRequestTarget target, Form dodajForm) {
                target.addComponent(resultLabel);
            }
        };
    }

    public SumaAjaxPage(PageParameters parameters) {
        super(parameters);
    }
}</pre>
<p>Metoda <em>onSubmit</em> w obiekcie klasy anonimowej dziedziczącej z <strong>AjaxButton</strong> jest wywoływana po naciśnięciu przycisku <strong>Sumuj</strong> w wyniku asynchronicznego zapytania z przeglądarki. W tej metodzie możemy zadeklarować które z komponentów powinny być odświeżone. Wicket wysyła w odpowiedzi fragmenty kodu <em>HTML</em> odpowiadające wybranym komponentom. Po stronie przeglądarki w odpowiedni sposób zmieniane jest drzewo DOM strony. Aby taka podmiana była możliwa, komponenty powinny posiadać atrybut <em>id</em>, który domyślnie nie jest obecny.  Aby to zmienić należy wykonać na odświeżanych komponentach w momencie ich tworzenia metodę <strong>setOutputMarkupId(true)</strong>. W naszym przykładzie oznacza to dodanie linii:</p>
<pre class="prettyprint"> resultLabel2.setOutputMarkupId(true);</pre>
<p>Na stronie głównej dodajemy link do SumaAjaxPage:</p>
<pre class="prettyprint">add(new PageLink("sumaAjaxLink", SumaAjaxPage.class));</pre>
<pre class="prettyprint">&lt;br/&gt;&lt;a wicket:id="sumaAjaxLink"&gt;Suma Ajax&lt;/a&gt;</pre>
<p>Po wejściu przez odnośnik Suma Ajax przyciśnięcie sumuj nie powoduje przeładowania strony, lecz tylko odświeżenie fragmentu z rezultatem. Dlaczego ten przykład działa skoro nie dodaliśmy pliku SumaAjaxPage.html? To kolejna przyjemna cecha Wicketa &#8211; dziedziczona może być nie tylko implementacja w Javie, ale też szablony HTML, po prostu strona SumaAjaxPage używa szablonu SumaPage.html !</p>
<p><strong>Testowanie</strong></p>
<p>Na stronie pojawił się element którego nie dodawaliśmy (odnośnik opisany <strong>WICKET AJAX DEBUG</strong>). Wynika to z tego że aplikacja jest uruchomiona jest w trybie <em>debug</em>. Po kliknięciu w ten odnośnik wyświetla się okno, w którym możemy zobaczyć wywołania AJAX z przeglądarki i odpowiedzi na te zapytania, coś co bardzo przydaje się przy debugowaniu bardziej złożonych aplikacji.</p>
<p>Wicket zawiera klasę WicketTester służącą do testów jednostkowych komponentów. Została napisana z użyciem asercji JUnit, ale działa również z TestNG. Za pomocą tego narzędzia w dużym stopniu można zamodelować sposób używania aplikacji przez użytkownika, można sprawdzić nie tylko pojedyncze komponenty ale też scenariusze obejmujące wiele stron WWW. Testowalne są również akcje AJAX!</p>
<p>Testy dla opisanych tu komponentów znajdują się w załączonym kodzie źródłowym.</p>
<p><strong>Co dalej?</strong></p>
<p>Wicket stosunkowo łatwo rozszerzać. Dowodem na to może być choćby zestaw zewnętrznych modułów zgrupowanych w projekcie <a href="http://wicketstuff.org/confluence/display/STUFFWIKI/Wiki">Wicket stuff </a>.</p>
<p>Dokumentacja do Wicket nie jest rewelacyjna i daleko jej do kompletności, ale otwarte kody źródłowe są na tyle dobrze napisane, że braki łatwo uzupełnić po prostu je przeglądając. W trudnych przypadkach warto zadać pytanie na liście dyskusyjnej wicket-user, odpowiedzi na dobrze zadane pytania przychodzą szybko :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/07/07/apache-wicket-troche-inny-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JasperReport &#8211; część II</title>
		<link>http://j2ee.pl/2008/07/04/jasperreport-czesc-ii/</link>
		<comments>http://j2ee.pl/2008/07/04/jasperreport-czesc-ii/#comments</comments>
		<pubDate>Fri, 04 Jul 2008 07:56:50 +0000</pubDate>
		<dc:creator>Dariusz Sot</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=164</guid>
		<description><![CDATA[Ze względu na fakt że zdecydowana większość aplikacji ma jako część funkcjonalności szeroko pojęte generowanie raportów &#8211; ponownie na warsztacie znalazł się Jasper. Tym razem jednak postaram się przedstawić sposób na proste tworzenie raportów wymagających nie tylko dynamicznej ilości wierszy, ale także i kolumn. Nie będę się skupiać na tematach omówionych w poprzednim artykule o [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/jasperreports/index.html"><img class="float" title="jr-logo" src="http://j2ee.pl/wp-content/uploads/2008/07/jr-logo.gif" alt="" width="196" height="65" align="left" /></a>Ze względu na fakt że zdecydowana większość aplikacji ma jako część funkcjonalności szeroko pojęte generowanie raportów &#8211; ponownie na warsztacie znalazł się Jasper. Tym razem jednak postaram się przedstawić sposób na proste tworzenie raportów wymagających nie tylko dynamicznej ilości wierszy, ale także i kolumn. Nie będę się skupiać na tematach omówionych w <a href="http://j2ee.pl/2008/05/30/jasperreports-i-spring/">poprzednim artykule o Jasperze</a> i omówię tylko to co jest tu &#8216;nowością&#8217;, łącznie z krótkim opisem użycia narzędzia iReport.<br />
<span id="more-164"></span></p>
<p><strong>Problem</strong></p>
<p>Stosunkowo łatwo można wyobrazić sobie raport, w którym musimy przedstawić dane w nieznanym z góry rozmiarze. O ile dodawanie nowych elementów w pionie jest trywialnym zagadnieniem w Jasperze, tak generowanie danych w poziomie jest już dość kłopotliwe. Raport z dynamiczną ilością zarówno kolumn jak i wierszy wydaje się być niemożliwym do zrobienia. Na szczęście iReport posiada dość nietypowy element który można umieścić na raporcie. Jest to tabela krzyżowa (crosstab). Jak można przypuszczać – służy ona do obsługi tych nietypowych sytuacji.</p>
<p><strong>Tworzenie szablonu raportu</strong></p>
<p>Zacznijmy zatem. Na początku potrzebne będzie nam DTO do przechowania danych które chcemy wyświetlić.</p>
<pre class="prettyprint">
public class ReportElementDTO {

   private String columnName;
   private String rowName;
   private Integer value;

    public String getColumnName() {
        return this.columnName;
    }
    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }
    public String getRowName() {
        return this.rowName;
    }
    public void setRowName(String rowName) {
        this.rowName = rowName;
    }
    public Integer getValue() {
        return this.value;
    }
    public void setValue(Integer value) {
        this.value = value;
    }
}
</pre>
<p>Następnie w iReporcie tworzymy sobie nowy raport i definiujemy w nim jako źródło danych przedstawioną powyżej strukturę (najprościej poprzez Data -> Report Query -> JavaBean Data Source):</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/datasource1.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/datasource1-300x206.jpg" alt="" title="datasource1" width="300" height="206" align="center" /></a></p>
<p>Następnie tworzymy Tabelę krzyżową. Pojawia się wizard, w którym mamy możliwość zdefiniowania wszystkich parametrów tabeli. Na początku wybieramy raport z którego nasza tabela będzie korzystała (obiekty, parametry, itd &#8211; czyli nasz dokument):</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/krok1.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/krok1-300x243.jpg" alt="" title="krok1" width="300" height="243" align="center" /></a></p>
<p>Następnie wybieramy parametr który ma być brany jako kolejne wiersze:</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/krok2.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/krok2-300x243.jpg" alt="" title="krok2" width="300" height="243" align="center" /></a></p>
<p>Następnie wybieramy parametr który ma być brany jako kolejne kolumny:</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/krok3.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/krok3-300x243.jpg" alt="" title="krok3" width="300" height="243" align="center" /></a></p>
<p>Kolejnym krokiem jest określenie zachowania części z danymi. Tu oprócz pola z danymi podajemy także funkcję która będzie odpowiedzialna za przetworzenie danych w kolumnach/wierszach. Tu najczęściej wykorzystywaną opcją jest suma:</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/krok4.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/krok4-300x243.jpg" alt="" title="krok4" width="300" height="243" align="center" /></a></p>
<p>Następnie określamy czy nasza tabela ma mieć kolumnę/wiersz podsumowujący oraz czy chcemy mieć widoczną siatkę pomiędzy polami:</p>
<p><a href='http://j2ee.pl/wp-content/uploads/2008/07/krok5.jpg'><img src="http://j2ee.pl/wp-content/uploads/2008/07/krok5-300x243.jpg" alt="" title="krok5" width="300" height="243" align="center" /></a></p>
<p>Pozostało tylko zatwierdzić podane powyżej informacje i mamy już gotową tabelę!</p>
<p><strong>Jak to działa?</strong></p>
<p>W wyniku powyższych czynności w iReporcie powstaje standardowy raport z tabelą krzyżową oraz dodatkowa karta właściwości tej tabeli. Tam można podejrzeć wszystkie podane wcześniej ustawienia i je zmodyfikować. Na tej części można także dodać dodatkowe informacje (na przykład zdefiniować kolumny podsumowań jako nazwy brane z Resource Bundle’a).</p>
<p>W jaki sposób zasilić taką tabelę danymi? Otóż jest to osiągane poprzez odpowiednią konstrukcję źródła danych. Jako dane przysyła się zwykłą jednowymiarową listę przedstawionych powyżej obiektów <em>ReportElementDTO</em>, sam Jasper zajmuje się odpowiednim przetworzeniem tej listy. W skrócie działa to tak że z jednowymiarowej listy jest tworzona dwuwymiarowa macierz, przy czym wartości są grupowane poprzez stwierdzenie ‘równości’ danych w odpowiednich property. W przypadku gdy zdefiniowaliśmy ‘columnName’ jako property odpowiadające za nazwę danej kolumny – dane się pojawią w kolejnych wierszach dla tych elementów z listy, które spełniają warunek ‘equals’ na danej wartości ‘columnName’. Analogiczna sytuacja jest w przypadku wierszy (tu odpowiada za to property ‘rowName’). Dodatkowo mozna zdefiniować swój własny sposób porównywania tych wartości, łącznie z kierunkiem sortowania. Logika nakazuje uważać że property ‘value’ odpowiada za wyświetlaną wartość. I tak właśnie jest, należy jednak pamiętać o wykorzystanej funkcji &#8211; w moim przypadku dzięki funkcji ‘sum’ pojawią się tu wartości będące sumą wszystkich wystąpień danego elementu. Jak np. na liście źródłowej znajdą się dwa identyczne z punktu widzenia tabeli krzyżowej dane (odpowiednio ‘columnName’ oraz ‘rowName’ są takie same) – wartości ‘value’ będą zsumowane. Poza tą funkcją są dostępne jeszcze inne ciekawe funkcje, choćby ‘count’, która po prostu liczy ilość wystąpień danego elementu.</p>
<p><strong>Aplikacja</strong></p>
<p>W celu zobrazowania możliwości tabel krzyżowych napisałem małą aplikację opartą o Springa. Jest to rozwinięcie poprzedniej aplikacji stworzonej na potrzeby wspomnianego już <a href="http://j2ee.pl/2008/05/30/jasperreports-i-spring/">poprzedniego artykułu o Jasperze</a>. Większość użytych rzeczy jest tam opisana. Aplikacja jest bardzo prosta, jej praca polega na przyjęciu od użytkownika wartości które zostaną następnie użyte do wygenerowania odpowiedniej wielkości tabeli krzyżowej. Całość kodu jaki powstał, łącznie z plikiem szablonu raportu w formacie &#8216;*.jrxml&#8217; jest dostępny pod <a href='http://j2ee.pl/wp-content/uploads/2008/07/jaspercrosstab.zip'>tym adresem</a>.</p>
<p><strong>Podsumowanie</strong></p>
<p>Tabela krzyżowa jest stosunkowo prostą konstrukcją, która w miarę przystępny sposób umożliwia definiowanie dynamicznych raportów. Po odpowiednim zaprojektowaniu szablonu jedyną większą trudnością jest właściwe przygotowanie źródła danych. Jak widać jednak z załączonego kodu, całość jest zdecydowanie mniej straszna niż by się wydawało. </p>
<p>Osobną kwestią jest samo narzędzie iReport. Mimo swoich wad jest to bardzo przydatne narzędzie i po pewnej praktyce staje się niesamowicie przydatny. Można w nim bardzo szybko stworzyć szablon raportu który po przekompilowaniu do formatu ‘*.jasper’ staje się gotowym do użycia plikiem. Uważam że iReport jest bardzo dobrym narzędziem do przygotowywania szablonów raportów. Mimo swoich wad bardzo pomaga w procesie tworzenia tych szablonów, dzięki czemu możemy się skupić na tym co tak bardzo lubimy – kodowaniu ;-) </p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/07/04/jasperreport-czesc-ii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Grails &#8211; koniec poszukiwań?</title>
		<link>http://j2ee.pl/2008/06/30/grails-koniec-poszukiwan/</link>
		<comments>http://j2ee.pl/2008/06/30/grails-koniec-poszukiwan/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 08:53:18 +0000</pubDate>
		<dc:creator>Jacek Byczyński</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=156</guid>
		<description><![CDATA[
Niespełna dwa lata temu zaczął powstawać nowy framework – Grails, służący do budowy aplikacji webowych oparty na  skryptowym języku programowania &#8211; Groovy. Od niedawna Grails coraz częściej przyciąga uwagę, a głównym powodem jest fakt, że prostą aplikacje webową można wykonać  kilkoma poleceniami  z konsoli, no może jeszcze dwie, trzy linijki kodu. Myślę, [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left; margin: 30px;" src="http://j2ee.pl/wp-content/uploads/2008/06/grailslogo.jpg" alt="" width="277" height="82" /><br />
Niespełna dwa lata temu zaczął powstawać nowy framework – Grails, służący do budowy aplikacji webowych oparty na  skryptowym języku programowania &#8211; Groovy. Od niedawna Grails coraz częściej przyciąga uwagę, a głównym powodem jest fakt, że prostą aplikacje webową można wykonać  kilkoma poleceniami  z konsoli, no może jeszcze dwie, trzy linijki kodu. Myślę, że warto przyjrzeć się nowemu rozwiązaniu, które upraszcza oraz przyspiesza tak znacznie prace i pokazać jego możliwości.</p>
<p> Na początek powiem kilka słów o samym Groovy, który jest określany czasem  nową, super wersją Javy. Jak można przypuszczać język ten  przejął wiele od swojego „rodzica”, wśród podobieństw na pewno  możemy wymienić taki sam model dziedziczenia, sposób tworzenia klas, pakietów,  metod.<span id="more-156"></span> Język ten jest dynamicznie typowany, działa na JVM, jest kompilowany do bytecodu  i bardzo dobrze integruję się z Java. Stosowanie niezdefiniowanych typów, ułatwia tworzenie i parsowanie XML, które jest silnie wspierane w Groovym. Dodatkowo uproszczone  jest testowanie przez wbudowanie Junit, obiektów mock. Zachęcam do szerszego zapoznania  się  z tym językiem, wszystkie potrzebne informacje można znaleźć na stronie &#8211;<br />
<a href="http://groovy.codehaus.org">http://groovy.codehaus.org</a>.<br />
Wracając do tematu  pracę z Grails rozpoczynamy od ściągnięcia frameworka  z <a href="http://www.grails.org/Installation">http://www.grails.org/Installation</a>.<br />
Wykonujemy instalacje według instrukcji na stronie, następnie  otwieramy konsole i jeśli wszystko działa po wpisaniu  polecenia <em>grails</em> otrzymujemy przywitanie   „Welcome to Grails 1.0.3 &#8230;” . W tym momencie mamy już wszystko co jest potrzebne do wykonania aplikacji webowej  opartej na modelu MVC. Nie musimy dodatkowo ściągać serwera , czy bazy danych. Grails wykorzystuje praktycznie wszystko co jest napisane i sprawdzone w Javie (Spring, Hibernate, SiteMesh, Ant, Jetty, Hsqldb). Główną ideą frameworka jest &#8220;Convention-over-configuration&#8221; co według założeń  powinno  znacznie przyspieszyć nam prace.<br />
W ramach zapoznania się z Grails  chciałbym wykonać  aplikację  do zarządzania użytkownikami  i ich prawami. Wykorzystam do tego plugin Acegi. Grails posiada wiele pluginow, które  możemy używać w zależności od potrzeb.<br />
Najpierw tworzymy skonfigurowaną aplikacje ,oraz gotowa strukturę katalogów poleceniem : <em>grails create-app user-app</em>. Po zaimportowaniu do eclipsa aplikacja wygląda następująco :<br />
<img src="http://j2ee.pl/wp-content/uploads/2008/06/grailsproject.jpg" alt="Grails struktura projektu" /><br />
Domyślnie mamy podpiętą bazę hsqldb, skonfigurowanego siteMesha, oraz pierwsze strony widoku z rozszerzeniem gsp. Jeżeli ktoś zna technologie jsp to  wystarczy zapoznać  się z nowym tagiem rozpoczynającym się od prefixu &lt;g:&#8230;&gt;, jest on odpowiednikiem popularnego w springu &lt;c:&#8230;&gt;.<br />
Aby skorzystać z pluginu Acegi należy go zainstalować poleceniem: <em>grails install-plugin acegi</em>, a następnie stworzyć klasy domenowe: <em>grails create-auth-domains User Role</em>.<br />
Przyjrzyjmy się teraz wygenerowanej klasie User:</p>
<pre class="prettyprint">
class User {
   static transients = ['pass']
   static hasMany = [authorities: Role]
   static belongsTo = Role

   /** Username */
   String username
   /** User Real Name*/
   String userRealName
   /** MD5 Password */
   String passwd
   /** enabled */
   boolean enabled

   String email
   boolean emailShow

   /** description */
   String description = ''

   /** plain password to create a MD5 password */
   String pass = '[secret]'

   static constraints = {
      username(blank: false, unique: true)
      userRealName(blank: false)
      passwd(blank: false)
      enabled()
   }
}</pre>
<p>W klasie tej mamy zdefiniowana relacje dzięki linijce: static hasMany = [authorities: Role], oraz walidacje: static constraints = {..} Możemy tudaj dodawać własne reguły walidacji np: size:5..15, tak aby nazwa nie była za krótka ani za długa.<br />
Nie mamy jeszcze kontrolerów, ani widoków do zarządzania logowaniem, w tym celu wykonujemy: <em>grails generate-manager</em> .Po wykonaniu tego polecenia mamy strony gsp oraz kontrolery CRUD(create ,read, update,delete) dla każdej klasy domenowej.<br />
Tworzymy jeszcze stronę główną naszej aplikacji, zaczynamy od kontrolera: <em>grails create-controller Welcome</em>.<br />
Wygenerowany kontroler modyfikujemy w następujący sposób:</p>
<pre class="prettyprint">
class WelcomeController {
    def index = { render(view:"welcome")}
}</pre>
<p>W katalogu user-app/grails-app/views/welcome tworzymy widok  welcome.gsp:</p>
<pre class="prettyprint">
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;user-app&lt;/title&gt;
		&lt;meta name=&quot;layout&quot; content=&quot;main&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1 style=&quot;margin-left:20px;&quot;&gt;Lista akcji:&lt;/h1&gt;
        &lt;div class=&quot;dialog&quot; style=&quot;margin-left:20px;width:60%;&quot;&gt;
            &lt;ul&gt;
              &lt;g :each var=&quot;c&quot; in=&quot;${grailsApplication.controllerClasses}&quot;&gt;
                    &lt;li class=&quot;controller&quot;&gt;&lt;g :link controller=&quot;${c.logicalPropertyName}&quot;&gt;${c.fullName}&lt;/g&gt;&lt;/li&gt;
              &lt;/g&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
 </pre>
<p>Teraz modyfikujemy index.gsp, żeby aplikacja startowała od strony powitalnej:</p>
<pre class="prettyprint">< % response.sendRedirect("welcome" ) %></pre>
<p>Możemy już uruchomic naszą aplikacje poleceniem: <em>grails run-app</em> i w przeglądarce podajemy adres : <a href="http://localhost:8080/user-app">http://localhost:8080/user-app</a>, otrzymujemy widok z dostępnymi  kontrolerami:<br />
<img src="http://j2ee.pl/wp-content/uploads/2008/06/grailswidok11.jpg" alt="Strona welcome" /><br />
Prace z aplikacją  zaczynamy od skonfigurowania  roli:<br />
<img src="http://j2ee.pl/wp-content/uploads/2008/06/grailswidok2.jpg" alt="Tworzenie roli" /><br />
Następnie dodajemy nowego użytkownika:<br />
<img src="http://j2ee.pl/wp-content/uploads/2008/06/grailswidok3.jpg" alt="Tworzenie użytkownika" /><br />
Na końcu tworzymy maping dla strony powitalnej:<br />
<img src="http://j2ee.pl/wp-content/uploads/2008/06/grailswidok4.jpg" alt="Tworzenie mapingu" /><br />
W tym momencie po otwarciu strony głównej pojawi się nam okienko logowania, aby uzyskać dostęp wprowadzamy login i hasło.<br />
 W zasadzie wszystko już mamy gotowe, nie wiele się napracowaliśmy, a część aplikacji jest gotowa, można jeszcze dodać message, aby wszystko było w języku polskim. Nie mamy też zaimplementowanej warstwy serwisowej, co wymaga dodatkowej pracy.<br />
Obecnie istnieje już wiele projektów wykonanych z sukcesem na Grails, myślę, że będzie ich bardzo szybko przybywać. O powodzeniu nowego frameworka na pewno będzie stanowić używanie sprawdzonych technologi i moc pluginów, które znacznie przyspieszają pracę. Wydaje mi się, że na tą chwile trudno o lepsze rozwiązanie i każdy szybko doceni usprawnienia oferowane przez Grails. </p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/06/30/grails-koniec-poszukiwan/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>JackRabbit</title>
		<link>http://j2ee.pl/2008/06/20/jackrabbit/</link>
		<comments>http://j2ee.pl/2008/06/20/jackrabbit/#comments</comments>
		<pubDate>Fri, 20 Jun 2008 12:57:46 +0000</pubDate>
		<dc:creator>Piotr Święciak</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=152</guid>
		<description><![CDATA[
Jackrabbit jest najpopularniejszą darmową implementacją JCR dla technologii Java. Ale zacznijmy od początku, co to jest JCR? Java Content Repository jest repozytorium treści o strukturze drzewiastej, które dzięki zdolności definiowania typów węzłów i liści, bardzo rozszerza możliwości tego narzędzia. Mamy kilka wbudowanych typów (np. katalog, plik, link ) ale możemy również sami definiować nowe typy. [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left; margin: 30px;" src="http://j2ee.pl/wp-content/uploads/2008/06/jlogo.gif" alt="" width="277" height="82" /><br />
Jackrabbit jest najpopularniejszą darmową implementacją JCR dla technologii Java. Ale zacznijmy od początku, co to jest JCR? Java Content Repository jest repozytorium treści o strukturze drzewiastej, które dzięki zdolności definiowania typów węzłów i liści, bardzo rozszerza możliwości tego narzędzia. Mamy kilka wbudowanych typów (np. katalog, plik, link ) ale możemy również sami definiować nowe typy. Kolejnymi możliwościami są: tekstowe przeszukiwanie czy zarządzanie wersjami. JCR jest wiec idealnym narzędziem do budowania systemów zarządzania treścią. W artykule jako przykład stworzymy aplikację, która będzie korzystać z możliwości wersjonowania.<br />
<span id="more-152"></span><br />
<strong><br />
Konfiguracja JCR<br />
</strong><br />
Zaczynamy od konfiguracji Jackrabbita na serwerze (w naszym przykładzie Tomcat6). Proces składa się z trzech etapów:</p>
<ol>
<li>Kopiujemy do katalogu lib na serwerze potrzebne biblioteki (w pliku pom.xml są wszystkie dependency, także można pobrać źródła aplikacji i maven pobierze nam potrzebne biblioteki):
<ul>
<li>jcr-1.0.jar</li>
<li>jackrabbit-api-1.4.jar</li>
<li>jackrabbit-core-1.4.jar</li>
<li>jackrabbit-jcr-commons-1.4.jar</li>
<li>jackrabbit-spi-1.4.jar</li>
<li>jackrabbit-spi-commons-1.4.jar</li>
<li>jackrabbit-text-extractors-1.4.jar</li>
<li>slf4j-api-1.3.0.jar</li>
<li>slf4j-simple-1.3.0.jar</li>
<li>commons-collections-3.1.jar</li>
<li>lucene-core-2.2.0.jar</li>
<li>derby-10.2.1.6.jar</li>
<li>concurrent-1.3.4.jar</li>
</ul>
</li>
<li>Konfiguracja pliku conf/server.xml. W GlobalNamingResources definiujemy Resource.
<pre class="prettyprint">&lt;Resource name="jcr/globalRepository" auth="Container" type="javax.jcr.Repository" factory="org.apache.jackrabbit.core.jndi.BindableRepositoryFactory" configFilePath="webapps/repo/repository.xml" repHomeDir="webapps/repo"/&gt;</pre>
<p>Parametr configFilePath definiuje ścieżkę dostępu do pliku konfiguracyjnego repozytorium &#8211; repository.xml. W repHomeDir podajemy ścieżkę do katalogu domowego repozytorium. W naszym przypadku widzimy ze repozytoriom znajduje się w katalogu webapps naszego serwera.</li>
<li>Konfiguracja pliku conf/context.xml
<pre class="prettyprint">&lt;ResourceLink name="jcr/repository" global="jcr/globalRepository" type="javax.jcr.Repository"/&gt;</pre>
</li>
</ol>
<p>Mamy już skonfigurowany serwer oraz podane ścieżki gdzie znajdować się będzie nasze repozytorium. Teraz musimy skonfigurować plik repository.xml, a następnie skopiować go w miejsce które podaliśmy w configFilePath.</p>
<pre class="prettyprint">&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.2//EN"
                            "http://jackrabbit.apache.org/dtd/repository-1.2.dtd"&gt;
&lt;Repository&gt;

    &lt;FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem"&gt;
        &lt;param name="path" value="${rep.home}/repository"/&gt;
    &lt;/FileSystem&gt;

    &lt;Security appName="Jackrabbit"&gt;

        &lt;AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager"&gt;
            &lt;!-- &lt;param name="config" value="${rep.home}/access.xml"/&gt; --&gt;
        &lt;/AccessManager&gt;

        &lt;LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule"&gt;
           &lt;!-- anonymous user name ('anonymous' is the default value) --&gt;
           &lt;param name="anonymousId" value="anonymous"/&gt;
           &lt;!--
              default user name to be used instead of the anonymous user
              when no login credentials are provided (unset by default)
           --&gt;
           &lt;!-- &lt;param name="defaultUserId" value="superuser"/&gt; --&gt;
        &lt;/LoginModule&gt;
    &lt;/Security&gt;

    &lt;Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/&gt;

	&lt;Workspace name="Jackrabbit Core"&gt;

        &lt;FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem"&gt;
            &lt;param name="path" value="${wsp.home}"/&gt;
        &lt;/FileSystem&gt;

        &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager"&gt;
          &lt;param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/&gt;
          &lt;param name="schemaObjectPrefix" value="Jackrabbit Core_"/&gt;
        &lt;/PersistenceManager&gt;

        &lt;SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex"&gt;
            &lt;param name="path" value="${wsp.home}/index"/&gt;
        &lt;/SearchIndex&gt;
    &lt;/Workspace&gt;

    &lt;Versioning rootPath="${rep.home}/version"&gt;

        &lt;FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem"&gt;
            &lt;param name="path" value="${rep.home}/version" /&gt;
        &lt;/FileSystem&gt;

        &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager"&gt;
          &lt;param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/&gt;
          &lt;param name="schemaObjectPrefix" value="version_"/&gt;
        &lt;/PersistenceManager&gt;

    &lt;/Versioning&gt;

    &lt;SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex"&gt;
        &lt;param name="path" value="${rep.home}/repository/index"/&gt;
    &lt;/SearchIndex&gt;
&lt;/Repository&gt;</pre>
<p>Mamy już skonfigurowany JCR. Teraz zajmiemy się nasza przykładową aplikacją.</p>
<p><strong>Aplikacja</strong><br />
Jako przykład stworzymy prostą aplikacje webową w oparciu o szkielet springa. Na stronie będzie przykładowy formularz, odzwierciedlający dokument, który będzie zapisywany a zarazem wersjonowany w naszym repozytorium. Oczywiście zaczynamy od stworzenia szkieletu projektu:</p>
<pre class="prettyprint">mvn archetype:create -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=pl.j2ee.jcr_example -DartifactId=jcr-example</pre>
<p>Kolejnym krokiem jest konfiguracja pliku pom.xml</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&lt;/groupId&gt;
	&lt;artifactId&gt;jcr-example&lt;/artifactId&gt;
	&lt;packaging&gt;war&lt;/packaging&gt;
	&lt;version&gt;1&lt;/version&gt;
	&lt;name&gt;jcr-example&lt;/name&gt;
	&lt;url&gt;http://maven.apache.org&lt;/url&gt;
	&lt;build&gt;
		&lt;plugins&gt;
			&lt;plugin&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;/plugins&gt;
	&lt;/build&gt;
	&lt;dependencies&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework&lt;/groupId&gt;
			&lt;artifactId&gt;spring&lt;/artifactId&gt;
			&lt;version&gt;2.5.4&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.4&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;taglibs&lt;/groupId&gt;
			&lt;artifactId&gt;standard&lt;/artifactId&gt;
			&lt;version&gt;1.1.2&lt;/version&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;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;org.apache.jackrabbit&lt;/groupId&gt;
			&lt;artifactId&gt;jackrabbit-api&lt;/artifactId&gt;
			&lt;version&gt;1.4&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.apache.jackrabbit&lt;/groupId&gt;
			&lt;artifactId&gt;jackrabbit-core&lt;/artifactId&gt;
			&lt;version&gt;1.4.4&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;javax.jcr&lt;/groupId&gt;
			&lt;artifactId&gt;jcr&lt;/artifactId&gt;
			&lt;version&gt;1.0&lt;/version&gt;
			&lt;scope&gt;provided&lt;/scope&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.slf4j&lt;/groupId&gt;
			&lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
			&lt;version&gt;1.0&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;log4j&lt;/groupId&gt;
			&lt;artifactId&gt;log4j&lt;/artifactId&gt;
			&lt;version&gt;1.2.8&lt;/version&gt;
		&lt;/dependency&gt;
	&lt;/dependencies&gt;
&lt;/project&gt;</pre>
<p>Teraz tworzymy prosty model naszego przykładowego dokumentu.</p>
<pre class="prettyprint">package pl.j2ee.jcr_example;
import java.io.Serializable;
import java.util.Date;

public class DocumentJcr implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;
    private String title;
    private Date date;
    private String text;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}</pre>
<p>Service do obsługi repozytorium</p>
<pre class="prettyprint">package pl.j2ee.jcr_example.service;

import ...

public class DocumentService implements IDocumentService {

    public final Session sessionLogin() {

        InitialContext ctx;
        try {
            ctx = new InitialContext();

            Context environment = (Context) ctx.lookup("java:comp/env");
            Repository repository = (Repository) environment.lookup("jcr/repository");

            return repository.login(new SimpleCredentials("username", "password".toCharArray()));

        } catch (NamingException e) {
            e.printStackTrace();

        } catch (RepositoryException e) {
            e.printStackTrace();
        }

        return null;
    }

    public final void sessionLogout(Session session) {
        if (session != null) {
            session.logout();
        }
    }

    public void addDocument(DocumentJcr documentJcr) {

        Session session = sessionLogin();
        try {

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obj = new ObjectOutputStream(out);
            obj.writeObject(documentJcr);
            obj.flush();
            InputStream ins = new ByteArrayInputStream(out.toByteArray());

            Node root = session.getRootNode();

            if (!root.hasNode("repozytorium/dokument")) {
                Node repozytorium = root.addNode("repozytorium");
                Node document = repozytorium.addNode("dokument", "nt:unstructured");

                document.addMixin("mix:versionable");
                document.setProperty("object", ins);

                session.save();

            } else {

                Node child = root.getNode("repozytorium/dokument");
                child.checkout();
                child.setProperty("object", ins);
                session.save();
                child.checkin();

            }

        } catch (Exception e) {
            // TODO: handle exception

        } finally {
            sessionLogout(session);
        }
    }

    public List getDocumentJcr() {
        Session session = sessionLogin();

        List listDocument = new ArrayList();
        try {

            Node root = session.getRootNode();
            Node node = root.getNode("repozytorium/dokument");

            VersionHistory versionHistory = node.getVersionHistory();
            VersionIterator versionIterator = versionHistory.getAllVersions();

            while (versionIterator.hasNext()) {
                Version version = versionIterator.nextVersion();
                NodeIterator nodeIterator = version.getNodes();

                while (nodeIterator.hasNext()) {

                    Node versionedNode = nodeIterator.nextNode();

                    if (versionedNode.hasProperty("object")) {

                        InputStream in = versionedNode.getProperty("object").getStream();
                        ObjectInputStream ois = new ObjectInputStream(in);

                        DocumentJcr document = (DocumentJcr) ois.readObject();
                        listDocument.add(document);
                    }
                }
            }

        } catch (Exception e) {
        } finally {
            sessionLogout(session);
        }
        return listDocument;
    }
}</pre>
<p>Jak widzimy w przykladzie dodajemy wezeł repozytorium do głównego wezła a następnie do niego kolejny &#8220;document&#8221; w ktorym będziemy zapisywać nasz prosty dokument. Ma on typ &#8220;nt:unstructured&#8221;, który cechuje się brakiem jakichkolwiek wymagań odnośnie swojej wewnętrznej budowy. Możemy więc tworzyć dowolną strukturę takiego węzła. Następnie dodajemy do węzła typ &#8220;mix:versionable&#8221;,co daje możliwość wersjonowania tego węzła (repozytorium przechowuje jego historie zmian). Podczas odczytu z tego węzła, dostaniemy ostatnią wersje dokumentu. Jednak możemy przeglądać wszystkie wersje naszego dukumentu iterując po VersionHistory dla konkretnego węzła. W naszym przykładzie cały czas odwołujemy się do jednego dokumentu (węzła). Wprowadzamy w nim zmiany, a następnie wszystkie wersje dokumentu wyświetlamy na stronie. Każdy węzeł posiada pola &#8220;property&#8221;  w których przechowujemy informacje w naszym przypadku obiekt DocumentJCR.</p>
<p>W pliku web.xml dodajemy wpis:</p>
<pre class="prettyprint">&lt;!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" &gt;

&lt;web-app&gt;

     ...
	&lt;resource-env-ref&gt;
		&lt;description&gt;Content Repository&lt;/description&gt;
		&lt;resource-env-ref-name&gt;jcr/repository&lt;/resource-env-ref-name&gt;
		&lt;resource-env-ref-type&gt;
			javax.jcr.Repository
		&lt;/resource-env-ref-type&gt;
	&lt;/resource-env-ref&gt;
     ...

&lt;/web-app&gt;</pre>
<p>Jeszcze kontroler i apliakcja gotowa</p>
<pre class="prettyprint">package pl.j2ee.jcr_example.controller;

public class MainController extends SimpleFormController {

    private IDocumentService documentService;

    public MainController() {
        setCommandClass(DocumentJcr.class);
    }

    @Override
    protected Map referenceData(HttpServletRequest request, Object command, Errors errors)
            throws Exception {

        HashMap map = new HashMap();

        map.put("documentList", documentService.getDocumentJcr());

        return map;
    }

    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command,
            BindException errors) throws Exception {

        DocumentJcr doc = (DocumentJcr) command;
        doc.setDate(new Date());
        documentService.addDocument(doc);

        return new ModelAndView("redirect:/main.action");
    }

    @Autowired
    public void setDocumentService(IDocumentService documentService) {
        this.documentService = documentService;
    }
}</pre>
<p>Oczywiscie to jest tylko mala czastka możliwosci jakie posiada Jackrabbit. Przedstawiłem jedynie jedną z możliwości czyli wersjonowanie. Warte zaznaczenia są jeszcze możliwość blokowania dostępu do danego węzła czy możliwość śledzenia zmian w repozytorium.<br />
Nasuwa się wniosek, że jeżeli mamy do czynienia z aplikacją do zarządzania treścią, napewno warto rozważyc mozliwość wykorzystania do tego biblioteki Jackrabbit.</p>
<p class="MsoNormal">Kod źródłowy aplikacji do pobrania <a href="http://j2ee.pl/wp-content/uploads/2008/06/jcr-example.zip">tutaj</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/06/20/jackrabbit/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>JasperReports i Spring</title>
		<link>http://j2ee.pl/2008/05/30/jasperreports-i-spring/</link>
		<comments>http://j2ee.pl/2008/05/30/jasperreports-i-spring/#comments</comments>
		<pubDate>Fri, 30 May 2008 08:59:41 +0000</pubDate>
		<dc:creator>Michał Gołacki</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=136</guid>
		<description><![CDATA[Jasper Reports jest to biblioteka pozwalająca na generowanie raportów. Jej głównym założeniem jest wsparcie dla tworzenia gotowych do druku dokumentów.  Biblioteka jest w całości napisana w Javie, pozwala to na użycie jej w dowolnej aplikacji Javy. Jasper oferuje eksport raportów do szeregu popularnych formatów takich jak: PDF, XML, HTML, CSV, XLS, RTF, TXT. W [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/jasperreports/index.html"><img class="float" title="jr-logo" src="http://j2ee.pl/wp-content/uploads/2008/05/jr-logo.gif" alt="" width="196" height="65" align="left" /></a>Jasper Reports jest to biblioteka pozwalająca na generowanie raportów. Jej głównym założeniem jest wsparcie dla tworzenia gotowych do druku dokumentów.  Biblioteka jest w całości napisana w Javie, pozwala to na użycie jej w dowolnej aplikacji Javy. Jasper oferuje eksport raportów do szeregu popularnych formatów takich jak: PDF, XML, HTML, CSV, XLS, RTF, TXT. W tym artykule zajmę się wykorzystaniem go do wygenerowania raportu w połączeniu ze Spring&#8217;iem.</p>
<p><span id="more-136"></span></p>
<p class="MsoNormal"><strong><span lang="PL">Szablon raportu</span></strong></p>
<p class="MsoNormal"><span lang="PL">Przede wszystkim potrzebny nam będzie plik z szablonem raportu, który wypełnimy danymi. Do jego przygotowania użyłem <a href="http://sourceforge.net/project/showfiles.php?group_id=64348">iReport</a></span><span lang="PL">. iReport jest edytorem WYSIWYG służącym do przygotowania pliku jrxml. Pozwala na ustalenie wszystkich parametrów szablonu, takich jak: rozmieszczenie pól, czcionki i wiele innych. iReport jest w stanie generować raporty samodzielnie, korzystając z wielu różnych źródeł danych.  Możliwości samego iReport są bardzo duże i nie będę poruszał tu wszystkich aspektów jego użycia, jednak postaram się przedstawić w skrócie zastosowane przeze mnie elementy. W szablonie pojawiają się <span style="color: #339966;">pola</span>, nazwane tak samo jak pola obiektu reprezentującego pojedynczy wiersz. Jasper wykorzysta metody getXXX, aby uzyskać dostęp ich wartości. Elementy zlokalizowane są tworzone z wykorzystaniem parametru <span style="color: #ff0000;">REPORT_RESOURCE_BUNDLE</span>, który pełni tu rolę MessageSource. Trzecia grupa elementów to nasze własne <span style="color: #ff0000;">parametry</span>, które zostaną przekazane w modelu. Ostatnia to <span style="color: #3366ff;">zmienne</span>, które służą na przykład do numerowania stron lub wierszy. Dokument jest podzielony na kilka sekcji. W tym zostało wykorzystane pięć z nich. Sekcja title, wyświetlana tylko na pierwszej stronie, zawiera jak sama nazwa wskazuje tytuł raportu, oczywiście w każdej sekcji możemy dowolnie zdefiniować wyświetlanie informacje, jednak należy pamiętać, że nie każda z nich zachowuje się tak samo. Sekcja pageHeader zawiera nagłówek wyświetlany na każdej stronie. W naszym przypadku będzie to data utworzenia raportu. Sekcja columnHeader zawiera zwykle nagłówki kolumn. W detail umieszczane są kolejne linie. Linie nie muszą być tylko liniami, mogą to być dużo bardziej skomplikowane konstrukcje. Dla każdego z wierszy (obiektu zawierającego dane) zostanie utworzona kopia takiego &#8220;wiersza&#8221;.  Ostatnią sekcją użytą w naszym przykładowym raporcie jest pageFooter, w której umieścimy numerację stron. </span><span lang="PL">Plik jrxml kompilujemy do formatu jasper i umieszczamy w projekcie.</span></p>
<p class="MsoNormal"><strong><span lang="PL">Spring</span></strong></p>
<p class="MsoNormal"><span lang="PL">Tu przede wszystkim potrzebny jest widok. W tym przypadku wykorzystamy klasę JasperReportsPdfView, która pozwoli nam na wygenerowanie raportu w postaci pliku PDF. </span></p>
<pre class="prettyprint">&lt;bean id="pdfView" class="org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView"&gt;
  &lt;property name="url" value="/WEB-INF/report/j2eereport.jasper" /&gt;
&lt;/bean&gt;</pre>
<p class="MsoNormal"><span lang="PL">W celu uzyskania raportów w innych formatach, będziemy musimy użyć odpowiadających im klas z pakietu org.springframework.web.servlet.view.jasperreports.</span></p>
<p class="MsoNormal"><span lang="PL">Musimy zdefiniować także messageSource. Zostanie on wykorzystany przy generowaniu zlokalizowanych raportów.</span></p>
<pre class="prettyprint">&lt;bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"&gt;
  &lt;property name="basenames"&gt;
    &lt;list&gt;
      &lt;value&gt;/WEB-INF/i18n/messages&lt;/value&gt;
    &lt;/list&gt;
  &lt;/property&gt;
  &lt;property name="defaultEncoding" value="UTF-8" /&gt;
  &lt;property name="cacheSeconds" value="1" /&gt;
&lt;/bean&gt;</pre>
<p class="MsoNormal">Wykorzystanie ReloadableResourceBundleMessageSource pozwoli na zastosowanie kodowania UTF-8 w plikach lokalizacyjnych.</p>
<p class="MsoNormal"><span lang="PL">Do generowania raportów w różnych językach przydadzą się nam localeResolver i localeChangeInterceptor<br />
</span></p>
<pre class="prettyprint">&lt;bean id="defaultLocale" class="java.util.Locale"&gt;
  &lt;constructor-arg value="pl" /&gt;
&lt;/bean&gt;

&lt;bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"&gt;
  &lt;property name="defaultLocale" ref="defaultLocale" /&gt;
&lt;/bean&gt;

&lt;bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"&gt;
  &lt;property name="paramName" value="lang" /&gt;
&lt;/bean&gt;</pre>
<p class="MsoNormal"><span lang="PL">Kolejne kroki to zdefiniowanie kontrolera, który obsłuży nasze zapytanie oraz serwisu, który dostarczy nam danych. W tej aplikacji serwis jest tylko zaślepką i zwraca nam statycznie zdefiniowane wartości w oczekiwanej przez nas ilości.</span></p>
<pre class="prettyprint">&lt;bean id="mainController" class="pl.j2ee.spring.jasperreport.controller.MainController"&gt;
  &lt;property name="formView" value="main"/&gt;
  &lt;property name="pdfView" ref="pdfView"/&gt;
  &lt;property name="salesDataService" ref="salesDataService"/&gt;
&lt;/bean&gt;</pre>
<p class="MsoNormal"><span lang="PL">Kontroler opakowuje wiersze w raporcie jako JRBeanCollectionDataSource i przekazuje je do widoku. Jasper już sam zajmie się wpisaniem ich w odpowiednie miejsca i wygenerowaniem właściwej liczby linii. Do modelu można dodać również inne parametry, takie jak chociażby data utworzenia raportu, czy użytkownik, który go wygenerował. Zwrócenie raportu do przeglądarki odbywa się podobnie jak przy zwracaniu dowolnego innego widoku, czyli przez utworzenie nowego obiektu ModelAndView. </span></p>
<pre class="prettyprint">protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception
{
  ReportFormDTO dto = (ReportFormDTO)command;
  Map&lt;String , Object&gt; model = new HashMap&lt;string , Object&gt;();
  List&lt;ReportLineDTO&gt; list = salesDataService.getSalesData(dto.getValue());
  model.put(“generatedOn”, new Date());
  model.put(“JRDataSource”, new JRBeanCollectionDataSource(list));
  return new ModelAndView(pdfView,model);
}</pre>
<p class="MsoNormal"><span lang="PL">Efektem będzie wygenerowanie zlokalizowanego dokumentu PDF zawierającego nasze dane. Aktualne locale pobrane zostanie z naszego localeResolver. Warto zauważyć, że lokalizowane są nie tylko teksty, ale także liczby i daty.</span></p>
<p class="MsoNormal">Pozostaje nam jeszcze spięcie wszystkiego w Spring&#8217;u i możemy już generować nasze raporty.</p>
<p class="MsoNormal"><span lang="PL">Z własnego doświadczenia zauważyłem, że dla każdego formatu pliku najlepiej jest przygotować osobny plik szablonu, ze względu na to iż na przykład Excel nie poradzi sobie poprawnie z separatorami tysięcy. Dokument przygotowany jako piękny pdf może być zupełnie bezużyteczny jako xls i odwrotnie.<br />
</span></p>
<p class="MsoNormal">Podsumowując moim zdaniem jest to doskonałe narzędzie do przygotowania dokumentów PDF, które będą wyglądały dokładnie tak jak sobie tego życzymy z dokładnością do pojedynczych pikseli, mimo iż zawarta w nich treść będzie generowana dynamicznie. Jak widać wygenerowanie pojedynczego raportu nie wymaga zbyt wiele kodu i polega głównie na przygotowaniu odpowiedniego szablonu.</p>
<p class="MsoNormal">Cały kod źródłowy aplikacji możecie znaleźć <a href="http://j2ee.pl/wp-content/uploads/2008/05/jasperreport.zip">tu</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/05/30/jasperreports-i-spring/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Stripes framework</title>
		<link>http://j2ee.pl/2008/04/16/stripes-framework/</link>
		<comments>http://j2ee.pl/2008/04/16/stripes-framework/#comments</comments>
		<pubDate>Wed, 16 Apr 2008 09:07:05 +0000</pubDate>
		<dc:creator>Piotr Święciak</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[JSP]]></category>
		<category><![CDATA[stripes]]></category>

		<guid isPermaLink="false">http://j2ee.pl/?p=125</guid>
		<description><![CDATA[
Stripes jest frameworkiem wspomagającym implementowanie warstwy prezentacji w aplikacjach webowych. Jest podobny do znanego wszystkim frameworka Struts. Jednak Stripesy nie wymagają konfigurowania warstwy widoku w plikach xml co w przypadku poznawania nowego frameworka jest kłopotliwe i wymaga więcej czasu. W tym wypadku cała konfiguracja obsługiwana jest za pomocą adnotacji wprowadzonych w Javie 5.

Jako przykład przedstawiający [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left; margin: 30px;" src="http://j2ee.pl/wp-content/uploads/2008/04/stripes3.png" alt="" width="160" height="42" /></p>
<p style="text-align: justify">Stripes jest frameworkiem wspomagającym implementowanie warstwy prezentacji w aplikacjach webowych. Jest podobny do znanego wszystkim frameworka Struts. Jednak Stripesy nie wymagają konfigurowania warstwy widoku w plikach xml co w przypadku poznawania nowego frameworka jest kłopotliwe i wymaga więcej czasu. W tym wypadku cała konfiguracja obsługiwana jest za pomocą adnotacji wprowadzonych w Javie 5.</p>
<p><span id="more-125"></span></p>
<p style="text-align: justify">Jako przykład przedstawiający kilka możliwości tego frameworka stworzymy prostą aplikację webową, która służyć będzie do zarządzania książkami w bibliotece (dodawanie nowych pozycji, usuwanie, edycja). Do stworzenia projektu będzie potrzebny właściwie tylko Maven2 oraz serwer aplikacji w naszym wypadku wystarczy Tomcat (oczywiście pomocny będzie również Eclipse choć nie jest konieczny).</p>
<p style="text-align: justify">Zaczynamy oczywiscie od wygenerowania szkieletu projektu :</p>
<p class="MsoNormal"><span lang="EN-US"><strong>mvn archetype:create -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=pl.jcommerce.biblioteka -DartifactId=Stripes-Biblioteka</strong></span></p>
<p class="MsoNormal">Następnie jeżeli chcemy korzystać z Eclipse w katalogu projektu wykonujemy instrukcje<strong> mvn eclipse:eclipse </strong>i importujemy projekt w Eclipse.</p>
<p class="MsoNormal">Kolejnym krokiem jest konfiguracja pliku pom.xml</p>
<pre class="prettyprint">&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
	xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
	xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
	&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
	&lt;groupId&gt;pl.jcommerce.biblioteka&lt;/groupId&gt;
	&lt;artifactId&gt;Stripes-Biblioteka&lt;/artifactId&gt;
	&lt;packaging&gt;war&lt;/packaging&gt;
	&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
	&lt;name&gt;stripesexample Maven Webapp&lt;/name&gt;
	&lt;url&gt;http://maven.apache.org&lt;/url&gt;
	&lt;build&gt;
		&lt;defaultGoal&gt;package&lt;/defaultGoal&gt;
		&lt;finalName&gt;stripesexample&lt;/finalName&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;/plugins&gt;
	&lt;/build&gt;
	&lt;dependencies&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;net.sourceforge.stripes&lt;/groupId&gt;
			&lt;artifactId&gt;stripes&lt;/artifactId&gt;
			&lt;version&gt;1.4.3&lt;/version&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;/dependencies&gt;
&lt;/project&gt;</pre>
<p>Teraz zajmiemy się konfiguracją deskryptora aplikacji webowej. W przypadku naszej aplikacji wystarczy najprostsza konfiguracja czyli ustawienie Stripes Filter oraz Stripes Dispatcher.</p>
<pre class="prettyprint">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;

&lt;web-app xmlns=&quot;http://java.sun.com/xml/ns/j2ee&quot;
	xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
	xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/j2ee
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd&quot;
	version=&quot;2.4&quot;&gt;
	&lt;filter&gt;
		&lt;display-name&gt;Stripes Filter&lt;/display-name&gt;
		&lt;filter-name&gt;StripesFilter&lt;/filter-name&gt;
		&lt;filter-class&gt;
			net.sourceforge.stripes.controller.StripesFilter
		&lt;/filter-class&gt;
	&lt;/filter&gt;
	&lt;filter-mapping&gt;
		&lt;filter-name&gt;StripesFilter&lt;/filter-name&gt;
		&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
		&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
	&lt;/filter-mapping&gt;
	&lt;filter-mapping&gt;
		&lt;filter-name&gt;StripesFilter&lt;/filter-name&gt;
		&lt;servlet-name&gt;StripesDispatcher&lt;/servlet-name&gt;
		&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
	&lt;/filter-mapping&gt;
	&lt;servlet&gt;
		&lt;servlet-name&gt;StripesDispatcher&lt;/servlet-name&gt;
		&lt;servlet-class&gt;
			net.sourceforge.stripes.controller.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;StripesDispatcher&lt;/servlet-name&gt;
		&lt;url-pattern&gt;*.action&lt;/url-pattern&gt;
	&lt;/servlet-mapping&gt;
&lt;/web-app&gt;</pre>
<p>Nasza aplikacja ma służyć do zarządzania książkami, dlatego zaczniemy od stworzenie modelu danych.</p>
<pre class="prettyprint">package pl.jcommerce.entity;
import java.util.Date;

public class Book {

	private int id;
	private String title;
	private String author;
	private Date date;
	private int amount;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	public int getAmount() {
		return amount;
	}

	public void setAmount(int amount) {
		this.amount = amount;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

}</pre>
<p style="text-align: justify">W końcu przyszedł czas na warstwą prezentacji, która zarządzana będzie przez framework Stripes. Kontrolery będą odpowiedzialne za akcje wykonywane przez użytkownika. Pierwszy z nich będzie miał za zadanie przekierowanie i wyświetlenie strony startowej, oraz stworzenie listy książek.<br />
Ponieważ nie korzystamy z bazy danych, dlatego wykorzystamy do tego celu statyczną listą książek, na której będziemy operować.</p>
<pre class="prettyprint">package pl.jcommerce.action;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;

@UrlBinding(&quot;/index.action&quot;)
public class IndexActionBean implements ActionBean {

	private ActionBeanContext context;

	public ActionBeanContext getContext() {
		return context;
	}

	public void setContext(ActionBeanContext context) {
		this.context = context;
	}

	@DefaultHandler
	public Resolution getListBooks() {
		return new ForwardResolution(ListBooksActionBean.class);
	}

}</pre>
<p style="text-align: justify">Kontroler ten jak i wszystkie inne implementuje interface ActionBean, ponieważ każdy kontroler musi zawierać pole context (informacje o aktualnym stanie aplikacji) typu ActionBeanConext. Adnotacja @UrlBinding(&#8221;/index.action&#8221;) przypisuje do kontrolera nazwę, według której będziemy sie odwoływać ze stron jsp. Kolejną adnotacją znajdującą się w tym kontrolerze jest @DefaultHandler. Metoda poprzedzona tą adnotają wykonana zostanie jeżeli będziemy się odwoływać do danego kontrolera, a nie podamy nazwy zdarzenia, które ma zostać obsłużone. W naszym wypadku w momencie odwołania sie do tego kontrolera zostaje wywołana domyślna metoda, która z kolei przekierowuje nas do kolejnego kontrolera ListBooksActionBean.</p>
<pre class="prettyprint">package pl.jcommerce.action;

import java.util.HashMap;
import java.util.Map;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import pl.jcommerce.entity.Book;

@UrlBinding(&quot;/listBooks.action&quot;)
public class ListBooksActionBean implements ActionBean {

	public static Map&lt;Integer, Book&gt; books = new HashMap&lt;Integer, Book&gt;();

	private ActionBeanContext context;

	public ActionBeanContext getContext() {
		return context;
	}

	public void setContext(ActionBeanContext context) {
		this.context = context;
	}

	@DefaultHandler
	public Resolution getListBooks() {
		return new ForwardResolution(&quot;/jsp/listBooks.jsp&quot;);
	}

	@HandlesEvent(&quot;addBook&quot;)
	public Resolution addBook() {
		return new ForwardResolution(AddBookActionBean.class);
	}

	public Map&lt;Integer, Book&gt; getBooks() {
		return books;
	}

	public void setBooks(Map&lt;Integer, Book&gt; books) {
		ListBooksActionBean.books = books;
	}

}</pre>
<p style="text-align: justify">W tym kontrolerze widzimy adnotację @HandlesEvent(&#8221;addBook&#8221;), która powoduje przypisanie nazwy zdarzenie do metody. Jeżeli przed metodą w kontrolerze nie użyjemy tej adnotacji  domyślne zdarzenia nazywają się tak samo jak nazwa metody. Ostatni kontroler w naszej aplikacji odpowiada za dodawanie i edycje książek.</p>
<pre class="prettyprint">package pl.jcommerce.action;import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.validation.LocalizableError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import net.sourceforge.stripes.validation.ValidationErrors;
import net.sourceforge.stripes.validation.ValidationMethod;
import pl.jcommerce.entity.Book;

@UrlBinding(&quot;/addBook.action&quot;)
public class AddBookActionBean implements ActionBean {

	private static final String SAVE_EVENT = &quot;save&quot;;

	private static int counter;

	@ValidateNestedProperties( {
	@Validate(field = &quot;title&quot;, on = SAVE_EVENT, required = true, minlength = 3, maxlength = 15),
	@Validate(field = &quot;author&quot;, on = SAVE_EVENT, required = true, maxlength = 25),
	@Validate(field = &quot;date&quot;, on = SAVE_EVENT, required = true, maxlength = 25),
	@Validate(field = &quot;amount&quot;, on = SAVE_EVENT, required = true) })
	private Book book;

	private ActionBeanContext context;

	public ActionBeanContext getContext() {
		return context;
	}

	public void setContext(ActionBeanContext context) {
		this.context = context;
	}

	public Book getBook() {
		return book;
	}

	public void setBook(Book book) {
		this.book = book;
	}

	@DefaultHandler
	public Resolution view() {
		return new ForwardResolution(&quot;/jsp/addBook.jsp&quot;);
	}

	@HandlesEvent(SAVE_EVENT)
	public Resolution save() {
		if (book.getId() == 0) {
			book.setId(++counter);
		}
		ListBooksActionBean.books.put(book.getId(), book);
		return new ForwardResolution(ListBooksActionBean.class);
	}

	@HandlesEvent(&quot;edit&quot;)
	public Resolution edit() {
		book = ListBooksActionBean.books.get(book.getId());
		return new ForwardResolution(&quot;/jsp/addBook.jsp&quot;);
	}

	@HandlesEvent(&quot;delete&quot;)
	public Resolution delete() {
		ListBooksActionBean.books.remove(book.getId());
		return new ForwardResolution(ListBooksActionBean.class);
	}

	@ValidationMethod(on = &quot;save&quot;)
	public void validateSave(ValidationErrors errors) {
		if (book.getId() == 0) {
			for (Book s : ListBooksActionBean.books.values()) {
				if (book.getTitle().equals(s.getTitle())) {
					errors.add(&quot;book.title&quot;, new LocalizableError(&quot;er.book.title&quot;));
					getContext().setValidationErrors(errors);
				}
			}
		}
	}

}</pre>
<p style="text-align: justify">W naszym ostatnim kontrolerze widzimy adnotacje związane z walidacją danych. Jeżeli mamy do czynienia z obiektem możemy walidować wszystkie jego pola oraz okreslić w przypadku jakich akcji dane pola maja być walidowane. W naszym przypadku wszystkie pola bedą sprawdzane w czasie zgłoszenia zdarzenia „save”.  Adnotacja @ValidateMethod(on=&#8221;save) powoduje wywołanie tej metody walidującej jedynie podczas zgłaszania zdarzenie &#8220;save&#8221;.</p>
<p style="text-align: justify">Walidacja oraz internacjonalizacja opisana jest w pliku StripesResources .properties</p>
<pre class="prettyprint">validation.required.valueNotPresent=Pole {0} jest wymagane polem
validation.minlength.valueTooShort=Pole {0} wymaga wpisania conajmniej {2} znakow
validation.maxlength.valueTooLong=Pole {0} moze miec max {2} znaki

converter.number.invalidNumber=Warto\u015B\u0107 ({1}) wprowadzona w polu "{0}" musi by\u0107 liczb\u0105
converter.date.invalidDate=Warto\u015B\u0107 ({1}) wprowadzona w polu "{0}" musi by\u0107 prawid\u0142ow\u0105 dat\u0105

book.title=Tytul
book.amount=Liczba dostepnych
book.date=Data wydania
book.author=Autor
er.book.title=Ksiazka o podanym tytule znajduje sie juz w bibliotece</pre>
<p style="text-align: justify">Wszystkie kontrolery już gotowe a więc teraz czas na strony jsp:</p>
<p style="text-align: justify">index.jsp</p>
<pre class="prettyprint">&lt;%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%&gt;
&lt;jsp:forward page="/index.action"/&gt;</pre>
<p class="MsoNormal" style="margin-bottom: 0.0001pt; line-height: normal;">listBooks.jsp</p>
<pre class="prettyprint">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&gt;
&lt;%@ taglib prefix=&quot;fn&quot; uri=&quot;http://java.sun.com/jsp/jstl/functions&quot;%&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot;%&gt;
&lt;%@ taglib prefix=&quot;stripes&quot; uri=&quot;http://stripes.sourceforge.net/stripes.tld&quot;%&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
	&lt;head&gt;
		&lt;title&gt;Stripes example&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;stripes:form action=&quot;/index.action&quot;&gt;
			&lt;stripes:submit name=&quot;addBook&quot; value=&quot;Add book&quot; /&gt;
			&lt;table&gt;
				&lt;tr&gt;
					&lt;td&gt;&lt;stripes:label for=&quot;book.title&quot; /&gt;&lt;/td&gt;
					&lt;td&gt;&lt;stripes:label for=&quot;book.author&quot; /&gt;&lt;/td&gt;
					&lt;td&gt;&lt;stripes:label for=&quot;book.date&quot; /&gt;&lt;/td&gt;
					&lt;td&gt;&lt;stripes:label for=&quot;book.amount&quot; /&gt;&lt;/td&gt;
				&lt;/tr&gt;
				&lt;c:forEach var=&quot;book&quot; items=&quot;${actionBean.books}&quot;&gt;
					&lt;tr&gt;
						&lt;td&gt;${book.value.title}&lt;/td&gt;
						&lt;td&gt;${book.value.author}&lt;/td&gt;
						&lt;td&gt;&lt;fmt:formatDate value=&quot;${book.value.date}&quot; pattern=&quot;yyyy-MM-dd&quot; /&gt;&lt;/td&gt;
						&lt;td&gt;${book.value.amount}&lt;/td&gt;
						&lt;td&gt;
							&lt;stripes:link href=&quot;/addBook.action&quot; event=&quot;edit&quot;&gt;Edit &lt;stripes:param name=&quot;book.id&quot; value=&quot;${book.value.id}&quot; /&gt;&lt;/stripes:link&gt;
						&lt;/td&gt;
						&lt;td&gt;
							&lt;stripes:link href=&quot;/addBook.action&quot; event=&quot;delete&quot;&gt;Delete&lt;stripes:param name=&quot;book.id&quot; value=&quot;${book.value.id}&quot; /&gt;&lt;/stripes:link&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
				&lt;/c:forEach&gt;
			&lt;/table&gt;
		&lt;/stripes:form&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre>
<p class="MsoNormal" style="margin-bottom: 0.0001pt; line-height: normal;">addBook.jsp</p>
<pre class="prettyprint">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&gt;
&lt;%@ taglib prefix=&quot;fn&quot; uri=&quot;http://java.sun.com/jsp/jstl/functions&quot;%&gt;
&lt;%@ taglib prefix=&quot;stripes&quot; uri=&quot;http://stripes.sourceforge.net/stripes.tld&quot;%&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;
&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
&lt;head&gt;
&lt;META http-equiv=&quot;contentType&quot; content=&quot;text/html;charset=UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;pl&quot;&gt;
&lt;title&gt;Stripes example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;stripes:form action=&quot;/addBook.action&quot;&gt;
	&lt;stripes:errors globalErrorsOnly=&quot;true&quot; /&gt;
	&lt;table&gt;
		&lt;tr&gt;
			&lt;td&gt;&lt;stripes:label for=&quot;book.title&quot; /&gt;:&lt;/td&gt;
			&lt;td&gt;&lt;stripes:text name=&quot;book.title&quot;&gt;&lt;/stripes:text&gt;&lt;/td&gt;
			&lt;td&gt;&lt;stripes:errors field=&quot;book.title&quot; /&gt;&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;&lt;stripes:label for=&quot;book.author&quot; /&gt;:&lt;/td&gt;
			&lt;td&gt;&lt;stripes:text name=&quot;book.author&quot;&gt;&lt;/stripes:text&gt;&lt;/td&gt;
			&lt;td&gt;&lt;stripes:errors field=&quot;book.author&quot; /&gt;&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;&lt;stripes:label for=&quot;book.date&quot; /&gt;:&lt;/td&gt;
			&lt;td&gt;&lt;stripes:text name=&quot;book.date&quot; formatPattern=&quot;yyyy-MM-dd&quot;&gt;&lt;/stripes:text&gt;&lt;/td&gt;
			&lt;td&gt;&lt;stripes:errors field=&quot;book.date&quot; /&gt;&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;&lt;stripes:label for=&quot;book.amount&quot; /&gt;:&lt;/td&gt;
			&lt;td&gt;&lt;stripes:text name=&quot;book.amount&quot;&gt;&lt;/stripes:text&gt;&lt;/td&gt;
			&lt;td&gt;&lt;stripes:errors field=&quot;book.amount&quot; /&gt;&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/table&gt;
	&lt;c:choose&gt;
		&lt;c:when test=&quot;${actionBean.book.id &gt; 0}&quot;&gt;
			&lt;stripes:hidden name=&quot;book.id&quot; value=&quot;book.id&quot;&gt;&lt;/stripes:hidden&gt;
		&lt;/c:when&gt;
		&lt;c:otherwise&gt;&lt;/c:otherwise&gt;
	&lt;/c:choose&gt;
	&lt;stripes:submit name=&quot;save&quot; value=&quot;Save&quot; /&gt;
&lt;/stripes:form&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p style="text-align: justify">Mamy już wszystkie pliki potrzebne nam w aplikacji zatem wykonujemy polecenie <strong> mvn install </strong> i plik war wrzucamy na serwer.<br />
Mam nadzieje, że ten krótki artykuł choć trochę przybliżył zasadę działania tego frameworka. Oczywiście możliwości jego są dużo większe niż przedstawiłem w tym artykule, dlatego na temat jego wszystkich możliwości odsyłam na stronę domową frameworka.</p>
<p class="MsoNormal" style="margin-bottom: 0.0001pt; line-height: normal;">
<p class="MsoNormal" style="margin-bottom: 0.0001pt; line-height: normal;">
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/04/16/stripes-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pluto, jetty i portlety.</title>
		<link>http://j2ee.pl/2008/03/26/pluto-jetty-i-portlety/</link>
		<comments>http://j2ee.pl/2008/03/26/pluto-jetty-i-portlety/#comments</comments>
		<pubDate>Wed, 26 Mar 2008 12:40:56 +0000</pubDate>
		<dc:creator>Karol Siczek</dc:creator>
				<category><![CDATA[Inne biblioteki]]></category>
		<category><![CDATA[jetty]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[pluto]]></category>
		<category><![CDATA[portlet]]></category>

		<guid isPermaLink="false">http://j2ee.pl/2008/03/26/pluto-jetty-i-portlety/</guid>
		<description><![CDATA[
Tytułem wstępu, czyli kto i co.
Witajcie.
To mój debiut na j2ee.pl i mam nadzieję, że będzie początkiem dłuższej przygody. W tym poście postaram się opisać jak, w kilku krokach, stworzyć i uruchomić prosty portlet używając mavena2 i kilku jego pluginów. Dlaczego to jest takie fajne? Ponieważ wystarczy wpisać mvn jetty:run i można testować swój portlet. Trwa [...]]]></description>
			<content:encoded><![CDATA[<h3><img src="http://j2ee.pl/wp-content/uploads/2008/03/pluto.thumbnail.gif" alt="Pluto" align="right" /></h3>
<h3>Tytułem wstępu, czyli kto i co.</h3>
<p>Witajcie.<br />
To mój debiut na j2ee.pl i mam nadzieję, że będzie początkiem dłuższej przygody. W tym poście postaram się opisać jak, w kilku krokach, stworzyć i uruchomić prosty portlet używając mavena2 i kilku jego pluginów. Dlaczego to jest takie fajne? Ponieważ wystarczy wpisać <em>mvn jetty:run</em> i można testować swój portlet. Trwa to kilka sekund, więc oszczędność czasu może być znacząca w porównaniu z restartem jBoss Portal, lub Liferay.  Ponieważ pomysłem było napisanie prostego howto, części teoretycznej niestety nie będzie. Nie znajdziecie tutaj co to jest portal/kontener portletów, ani nawet sam portlet. Zainteresowane osoby odsyłam do specyfikacji JSR168 i wszechwiedzącej sieci.<br />
<span id="more-117"></span></p>
<h3>Co będzie potrzebne?</h3>
<p>Właściwie tylko maven2 i jakiś edytorek kodu (zakładam, że w miarę nową jave każdy ma). Razem z m2 będziemy używać pluginów jetty i pluto. Jetty jest implementacją serwera webowego, natomiast pluto jest referencyjną implementacją specyfikacji portletów.</p>
<h3>Tworzymy projekt.</h3>
<p>Do wygenerowania szkieletu projektu wykorzystamy archetyp <em>maven-archetype-portlet</em>:<br />
<em><strong>mvn archetype:create -DgroupId=pl.j2ee.portlet -DartifactId=portlet-example -DarchetypeArtifactId=maven-archetype-portlet</strong></em><br />
Jeśli zamierzamy używać Eclipse&#8217;a, przechodzimy do katalogu projektu i wykonujemy:  <em><code><br />
</code><strong>mvn eclipse:eclipse</strong></em></p>
<p>Kolejną rzeczą, jaką należy wykonać, jest modyfikacja pom.xml projektu. Proponuję stworzyć oddzielny profil, np. <strong>pluto-embedded</strong>:</p>
<pre class="prettyprint">
&lt;profile&gt;
  &lt;id&gt;pluto-embedded&lt;/id&gt;
  [...]
&lt;/profile&gt;</pre>
<p>i w nim dodać wpisy dla pluginów:</p>
<pre class="prettyprint">&lt;plugin&gt;
  &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
  &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
&lt;/plugin&gt;</pre>
<p>oraz</p>
<pre class="prettyprint">&lt;plugin&gt;
  &lt;groupId&gt;org.apache.pluto&lt;/groupId&gt;
  &lt;artifactId&gt;maven-pluto-plugin&lt;/artifactId&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;phase&gt;generate-resources&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;assemble&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
&lt;/plugin&gt;</pre>
<p>Goal <strong>assemble</strong> posłuży do wygenerowania web.xml z odpowiednią konfiguracją i mapowaniem serwletu org.apache.pluto.core.PortletServlet.  Kolejną rzeczą jest konfiguracja pluginu jetty:</p>
<pre class="prettyprint">&lt;configuration&gt;
  &lt;webXml&gt;${project.build.directory}/pluto-resources/web.xml&lt;/webXml&gt;
  &lt;webDefaultXml&gt;src/main/webapp/WEB-INF/jetty-pluto-web-default.xml&lt;/webDefaultXml&gt;
  &lt;systemProperties&gt;
    &lt;systemProperty&gt;
      &lt;name&gt;org.apache.pluto.embedded.portletId&lt;/name&gt;
      &lt;value&gt;MyPortlet&lt;/value&gt;
    &lt;/systemProperty&gt;
  &lt;/systemProperties&gt;
&lt;/configuration&gt;</pre>
<p>Tag &lt;webXml&gt; wskazuje na miejsce, z którego jetty zaczyta sobie deskryptor web.xml-a (wygenerowany wcześniej przez pluto).<br />
Ponieważ pluto korzysta z pliku src/main/webapp/WEB-INF/web.xml do generowania pliku wynikowego, a my nie będziemy potrzebować dodatkowych mapowań, usuwamy to co stworzył nam archetyp i zostawiamy ten plik w postaci:</p>
<pre class="prettyprint">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
 &lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;/web-app&gt;</pre>
<p>Kolejna rzecz, której potrzebujemy, to konfiguracja parametrów kontekstu, filtrów, serwletów, mapowań itp. Najwygodniej jest użyć gotowego pliku jetty-pluto-web-default.xml, który można znaleźć w archiwum maven-jetty-pluto-embedded-1.0.jar, lub w załączonych źródłach projektu.</p>
<p>Przejdźmy więc do edycji pliku portlet.xml. Jego zawartość zawiera większość potrzebnych informacji, ale zawiera też błędy &#8211; tag &lt;portlet-class&gt; jest nieprawidłowo stworzony przez archetyp i należy go nieznacznie zmodyfikować wpisując poprawną nazwę klasy portletu. Zmieniamy także wartość tagu &lt;portlet-name&gt; tak, aby odpowiadała property org.apache.pluto.embedded.portletId z pom.xml projektu.  Na chwilę jeszcze wróćmy do pom.xml projektu i uzupełnijmy zależności:</p>
<pre class="prettyprint">
&lt;dependency&gt;
  &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
  &lt;artifactId&gt;jetty&lt;/artifactId&gt;
  &lt;version&gt;6.1.5&lt;/version&gt;
  &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
  &lt;artifactId&gt;jsp-2.1&lt;/artifactId&gt;
  &lt;version&gt;6.1.5&lt;/version&gt;
  &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;com.bekk.boss&lt;/groupId&gt;
  &lt;artifactId&gt;maven-jetty-pluto-embedded&lt;/artifactId&gt;
  &lt;version&gt;1.0&lt;/version&gt;
&lt;/dependency&gt;</pre>
<p>Aby sprawdzić, czy wszystko zrobiliśmy dobrze, przechodzimy do katalogu projektu i wpisujemy:</p>
<p><strong><em>mvn jetty:run -P pluto-embedded</em></strong></p>
<p>Gdy uruchomi nam się serwer, wpisujemy w przeglądarce <a href="http:localhost:8080/portlet-example/pluto/index.jsp" target="_blank">http:localhost:8080/portlet-example/pluto/index.jsp</a> i  <strong>voilà!</strong><br />
Jeśli komuś nie odpowiada port 8080 na którym słucha serwer, może zmodyfikować <a href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin" target="_blank">konfigurację pluginu</a>.  Teraz, gdy już widzimy, że wszystko w miarę fajnie się prezentuje zrobimy prosty formularz powitalny:  W pliku normal.jsp zamiast powitania mavena, stwórzmy formularz:</p>
<pre class="prettyprint">
&lt;c:choose&gt;
  &lt;c:when test="${not empty param.fullName}"&gt;
    Hello &lt;c:out value="${param.fullName}"&gt;&lt;/c:out&gt;
  &lt;/c:when&gt;
  &lt;c:otherwise&gt;
    &lt;form action="&lt;portlet:actionURL/&gt;" method="post" id="nameForm"&gt;
      &lt;table&gt;
        &lt;tr&gt;&lt;td&gt;First name:&lt;/td&gt;&lt;td&gt;&lt;input name="firstName" type="text" id="firstName"/&gt;&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Last name:&lt;/td&gt;&lt;td&gt;&lt;input name="lastName" type="text" id="lastName"/&gt;&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;input name="submit" type="submit" id="submit"/&gt;&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
    &lt;/form&gt;
  &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</pre>
<p>Następnie w klasie MyPortlet dodajemy prostą metodę:</p>
<pre class="prettyprint">
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {
	if(request.getParameter("firstName") != null &amp;&amp; request.getParameter("lastName") != null)
		response.setRenderParameter("fullName", request.getParameter("firstName") + " " + request.getParameter("lastName"));
}</pre>
<h3>Podsumowanie.</h3>
<p>Ponieważ, jak już wspomniałem, jest to mój pierwszy post na j2ee.pl (dzięki Michał :) ), będę wdzięczny za wszystkie komentarze. Piszcie, co Wam się podobało, co nie i czy takie how-to do czegoś Wam się przydało. Może coś jest niejasne, albo czegoś brakuje&#8230;W następnym poście postaram się dodać testy jwebunit-owe i pokazać w jaki sposób można taki formularz potraktować testami jednostkowymi.</p>
<h3>Źródła:</h3>
<p><a href="http://j2ee.pl/wp-content/uploads/2008/03/portlet-example_src.zip" title="Pluto, jetty i portlety-źródła.">Pluto, jetty i portlety-źródła</a>, <a href="http://www.mortbay.org/" target="_blank">jetty</a>, <a href="http://portals.apache.org/pluto/" target="_blank">pluto</a>, <a href="http://portletwork.blogspot.com/" target="_blank">portletwork</a>,</p>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2008/03/26/pluto-jetty-i-portlety/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
