<?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>dev garden &#187; Projektowanie aplikacji</title>
	<atom:link href="http://darekzon.com/category/projektowanie-aplikacji/feed" rel="self" type="application/rss+xml" />
	<link>http://darekzon.com</link>
	<description>when technology meets nature</description>
	<lastBuildDate>Mon, 19 Mar 2012 23:02:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Globalne łapanie wyjątków w Spring 3.1</title>
		<link>http://darekzon.com/2012/03/globalne-lapanie-wyjatkow-w-spring-3-1</link>
		<comments>http://darekzon.com/2012/03/globalne-lapanie-wyjatkow-w-spring-3-1#comments</comments>
		<pubDate>Mon, 19 Mar 2012 23:01:40 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[exception]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[spring3.1]]></category>
		<category><![CDATA[wyjątki]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=1416</guid>
		<description><![CDATA[Wielokrotnie wchodząc na przeróżne strony zamiast oczekiwanej przeze mnie treści pojawiała się strona z błędami aplikacji, i nie był to tylko komunikat w stylu &#8222;Błąd strony&#8221; a pełne kody błędów, łącznie ze stacktracem czy też kodem strony (jeśli była pisana &#8230; <a href="http://darekzon.com/2012/03/globalne-lapanie-wyjatkow-w-spring-3-1">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Wielokrotnie wchodząc na przeróżne strony zamiast oczekiwanej przeze mnie treści pojawiała się strona z błędami aplikacji, i nie był to tylko komunikat w stylu &#8222;Błąd strony&#8221; a pełne kody błędów, łącznie ze stacktracem czy też kodem strony (jeśli była pisana w języku skryptowym). Nie trzeba chyba nikomu tłumaczyć, że tego typu treść nigdy nie powinna być widoczna dla użytkownika końcowego. I nie tylko dlatego, że strona taka brzydko wygląda i może odstraszyć użytkowników. Treści wyjątków jakie powoduje aplikacja, czy też część kodu jakie pojawią się mogą być bez problemu wykorzystane przez osoby interesujące się zabezpieczeniami.<span id="more-1416"></span></p>
<p>W bibliotece SpringFramework, istnieje coś takiego jak ExceptionHandler, który działa lokalnie i jest implementowany per kontroler co przy większej aplikacji może być mało wygodne. Niestety globalny mechanizm przechwytywania powstanie najwcześniej w wersji 3.2(kiedyś przeczytałem pewien komentarz programisty Springa w ich systemie Jira) więc puki co musimy radzić sobie sami. Prosty system przechwytujący wyjątki można napisać samemu i nawet specjalnie się przy tym nie napracujemy.<br />
Do stworzenia takiego systemu będziemy potrzebować najnowszych bibliotek Springa oraz bibliotek wspomagających programowanie aspektowe (aspectjweaver)</p>
<p>Na początek skonfigurujemy klasę która będzie obsługiwać wyjątki, konfigurację umieszczamy w applicationContext.xml</p>
<pre class="brush: xml; title: ; notranslate">&lt;/pre&gt;
&lt;aop:aspectj-autoproxy /&gt;
 &lt;aop:config&gt;
 &lt;aop:aspect id=&quot;exceptionHandler&quot; ref=&quot;globalExceptionHandler&quot;&gt;
 &lt;!-- find all methods that returns ModelAndView objects --&gt;
 &lt;aop:pointcut expression=&quot;execution(org.springframework.web.servlet.ModelAndView+ *(..))&quot; id=&quot;returningMav&quot; /&gt;
 &lt;!-- and execute them inside exceptionHandler --&gt;
 &lt;aop:around method=&quot;exceptionHandler&quot; pointcut-ref=&quot;returningMav&quot; /&gt;
 &lt;/aop:aspect&gt;
 &lt;/aop:config&gt;
 &lt;bean id=&quot;globalExceptionHandler&quot; class=&quot;com.darekzon.spring.exhandler.aop.ExceptionHandler&quot; /&gt;
&lt;pre&gt;</pre>
<p>Powyższy zapis konfiguruje system tak aby wyłapał wszystkie metody zwracające obiekt &#8222;ModelAndView&#8221; (czyli zazwyczaj metody mapujące URL-e) i przekazywał je do naszego obiektu, a dokładniej do metody &#8222;exceptionHandler&#8221; w klasie &#8222;ExceptionHandler&#8221;.<br />
Metody te będą przekazywane w formie obiektu ProceedingJoinPoint posiadającego metodę &#8222;proceed&#8221; która służy do wywołania domyślnej akcji.</p>
<p>Nasza metoda obsługująca wyjątki wygląda jak poniżej</p>
<pre class="brush: java; title: ; notranslate">
public ModelAndViewexceptionHandler(ProceedingJoinPoint pjp)throws Throwable{
        ModelAndView mav=newModelAndView();
        try{
            mav=(ModelAndView) pjp.proceed();
        }catch(Exceptionae){
            mav.addObject(&quot;exceptionMessage&quot;,ae.getMessage());
            mav.setViewName(&quot;exception&quot;);
        }
        return mav;
    }
</pre>
<p>Działanie jest proste, wywołaj metodę która może rzucić wyjątek (tj. metodę obsługującą url) i jeśli taki wyjątek zostanie rzucony, złap go, dodaj jego wiadomość do widoku jako &#8216;exceptionMessage&#8217; oraz wygeneruj widok o nazwie &#8216;exception&#8217;. A co za tym idzie, jeśli nasza metoda kontrolera wyrzuci wyjątek, zostanie on złapany a zamiast niego zostanie zwrócony poprawny obiekt ModelAndView.</p>
<p>Oczywiście zamiast prostej podmiany widoków możemy przekierować użytkownika do innej strony, zapisać logi, czy też wysłać maila.</p>
<h3>Działający projekt</h3>
<blockquote><p>Działający projekt można pobrać przeglądać pod adresem <a title="Spring 3.1 globalne łapanie wyjątków" href="https://github.com/darek/spring.3.examples/tree/master/aop-global-exception-handler" target="_blank">https://github.com/darek/spring.3.examples/tree/master/aop-global-exception-handler</a></p></blockquote>
<div class="shr-publisher-1416"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2012/03/globalne-lapanie-wyjatkow-w-spring-3-1/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Atlassian Bitbucket &#8211; wreszcie wsparcie dla GIT-a</title>
		<link>http://darekzon.com/2011/10/atlassian-bitbucket-wreszcie-wsparcie-dla-git-a</link>
		<comments>http://darekzon.com/2011/10/atlassian-bitbucket-wreszcie-wsparcie-dla-git-a#comments</comments>
		<pubDate>Mon, 03 Oct 2011 19:48:15 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[bitbucket]]></category>
		<category><![CDATA[free]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[hosting]]></category>
		<category><![CDATA[mercurial]]></category>
		<category><![CDATA[private]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=1388</guid>
		<description><![CDATA[Jak dowiedziałem się przed chwilą, Atlassian wypuścił nową wersję swojego serwisu Bitbucket który służył jako hosting dla naszych projektów. Jeszcze do niedawna Bitbucket obsługiwał jedynie Mercurial jednak to się zmieniło. Wraz z nową wersją serwisu doszła obsługa Git-a co bardzo &#8230; <a href="http://darekzon.com/2011/10/atlassian-bitbucket-wreszcie-wsparcie-dla-git-a">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Jak dowiedziałem się przed chwilą, Atlassian wypuścił nową wersję swojego serwisu <a title="Bitbucket" href="https://bitbucket.org/" target="_blank">Bitbucket</a> który służył jako hosting dla naszych projektów. Jeszcze do niedawna Bitbucket obsługiwał jedynie Mercurial jednak to się zmieniło. Wraz z nową wersją serwisu doszła obsługa Git-a co bardzo mnie cieszy. Prócz wsparcia dla <a title="GIT" href="http://gitscm.com/" target="_blank">Git</a>-a mamy możliwość zintegrowania z wieloma serwisamy (Jenkins &#8211; dawny Hudson, Jira, Twitter itd.)</p>
<p><span id="more-1388"></span>Jak dla mnie to duży krok, do tej pory w moim przekonaniu nie istniał żaden serwis (godny zaufania) udostępniający darmowe prywatne repozytoria dla Git-a (co prawda <a title="Beanstalkapp" href="http://beanstalkapp.com/" target="_blank">Beanstalkapp</a> ma darmowe konta nazwane Trial ale są mocno ograniczone) na taką skalę. Z dużym prawdopodobieństwem bitbucket wprowadzi odrobinę zamieszania.</p>
<p>&nbsp;</p>
<div class="shr-publisher-1388"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2011/10/atlassian-bitbucket-wreszcie-wsparcie-dla-git-a/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Cloud Foundry &#8211; chmurki wyrastają jak grzyby po deszczu</title>
		<link>http://darekzon.com/2011/05/cloud-foundry-chmurki-wyrastaja-jak-grzyby-po-deszczu</link>
		<comments>http://darekzon.com/2011/05/cloud-foundry-chmurki-wyrastaja-jak-grzyby-po-deszczu#comments</comments>
		<pubDate>Thu, 05 May 2011 22:25:49 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[Chmury]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[chmury]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[programowanie]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[sinatra]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[vmware]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=1187</guid>
		<description><![CDATA[Nie tak dawno temu przedstawiałem nowy serwis udostępniający chmurę obliczeniową od CloudBees a już na horyzoncie pojawia się nowy zawodnik. Tym razem mamy do czynienia z produktem Cloud Foundry firmy VMware. Cloud Foundry umożliwia uruchomienie aplikacji stworzonych z wykorzystaniem takich &#8230; <a href="http://darekzon.com/2011/05/cloud-foundry-chmurki-wyrastaja-jak-grzyby-po-deszczu">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p style="text-align: center;"><img class="size-full wp-image-1189 aligncenter" title="Cloud Foundry" src="http://www1.darekzon.com/wp-content/uploads/2011/05/cloud_foundry_logo.png" alt="Cloud Foundry" width="650" height="83" /></p>
<p>Nie tak dawno temu przedstawiałem <a title="Hudson-ci „za darmo” – beta od CloudBees" href="http://darekzon.com/2011/01/hudson-ci-za-darmo-beta-od-cloudbees">nowy serwis udostępniający chmurę obliczeniową od CloudBees</a> a już na horyzoncie pojawia się nowy zawodnik. Tym razem mamy do czynienia z produktem <strong>Cloud Foundry </strong>firmy <a title="VMware" href="http://www.vmware.com/" target="_blank">VMware</a>.<br />
<strong>Cloud Foundry</strong> umożliwia uruchomienie aplikacji stworzonych z wykorzystaniem takich frameworków jak <a title="Spring Framework" href="http://www.springsource.org/" target="_blank">Spring</a>, <a title="Ruby on Rails" href="http://rubyonrails.org/" target="_blank">Rails</a>, <a title="Node.js" href="http://nodejs.org/" target="_blank">Node.js</a>, <a title="Grails Framework" href="http://grails.org/" target="_blank">Grails</a> oraz <a title="Sinatra Framework" href="http://www.sinatrarb.com/" target="_blank">Sinatra</a>. <span id="more-1187"></span></p>
<p>Usługa <strong>Cloud Foundry</strong> podzielona jest na 3 części:</p>
<ul>
<li>CloudFoundry.com  - kompletne komercyjne środowisko  udostępnione na zasadzie <a title="PaaS - Platform as a Service" href="http://pl.wikipedia.org/wiki/Platform_as_a_Service" target="_blank">PaaS</a></li>
<li>CloudFoundry.org &#8211; otwarty projekt w którym każdy może poprawić kod aplikacji CloudFoundry (kod źródłowy dostepny pod adresem: <a title="Cloud Foundry - kod źródłowy" href="https://github.com/cloudfoundry" target="_blank">https://github.com/cloudfoundry</a> )</li>
<li>CloudFoundry Micro Cloud &#8211; virtualna maszyna z prywatnym CloudFoundry do uruchomienia na własnych komputerze</li>
</ul>
<p>Na dzień dzisiejszy serwis jest w fazie testów i żeby wypróbować jego możliwości trzeba <a title="Cloud Foundry - rejestracja" href="http://www.cloudfoundry.com/signup" target="_blank">zapisać się</a> i czekać na zaproszenie (ja czekałem 2 tygodnie).</p>
<p>Dodatkowe informacje na temat projektu znajdziecie na <a title="VMware - Cloud Foundry - Youtube" href="http://www.youtube.com/user/vmwaretv?feature=mhum#p/c/71331D676E1D17CA" target="_blank">kanale Youtube</a>.</p>
<div class="shr-publisher-1187"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2011/05/cloud-foundry-chmurki-wyrastaja-jak-grzyby-po-deszczu/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Oracle Cloud Computing Summit – Warszawa</title>
		<link>http://darekzon.com/2011/01/oracle-cloud-computing-summit-warszawa</link>
		<comments>http://darekzon.com/2011/01/oracle-cloud-computing-summit-warszawa#comments</comments>
		<pubDate>Tue, 25 Jan 2011 09:32:38 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[cloud computing]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[konferencja]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[spotkanie]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=1152</guid>
		<description><![CDATA[Oracle przy współpracy z Intel-em organizuje 27 stycznia spotkanie na temat przetwarzania w chmurze. Każdy uczestnik będzie mógł dowiedzieć się jak zbudować nowoczesną architekturę przetwarzania w chmurze, zobaczyć przykładowe wdrożenia, czy też poznać najlepsze najlepsze procedury sprawdzone w sytuacjach rzeczywistych. &#8230; <a href="http://darekzon.com/2011/01/oracle-cloud-computing-summit-warszawa">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p style="text-align: center;"><img class="aligncenter size-medium wp-image-1153" title="Oracle" src="http://www2.darekzon.com/wp-content/uploads/2011/01/imgres-300x37.jpg" alt="" width="300" height="37" /></p>
<p>Oracle przy współpracy z Intel-em organizuje 27 stycznia spotkanie na temat przetwarzania w chmurze. Każdy uczestnik będzie mógł dowiedzieć się jak zbudować nowoczesną architekturę przetwarzania w chmurze, zobaczyć przykładowe wdrożenia, czy też poznać najlepsze najlepsze procedury sprawdzone w sytuacjach rzeczywistych. Spotkanie odbywa się w hotelu Raddison BLU przy ulicy Grzybowskiej 24 w Warszawie.</p>
<p>Więcej informacji oraz możliwość zarejestrowania się (liczba miejsc ograniczona) dostępne na <a title="Oracle Cloud Computing Summit" href="http://eventreg.oracle.com/webapps/events/ns/EventsDetail.jsp?p_eventId=126516&amp;src=7021970&amp;src=7021970&amp;Act=22" target="_blank">stronie spotkania</a></p>
<div class="shr-publisher-1152"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2011/01/oracle-cloud-computing-summit-warszawa/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spring Framework 3.0 Tutorial – cz 4 – sitemesh, menu, atrybuty kontekstu</title>
		<link>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-4-%e2%80%93-sitemesh-menu-atrybuty-kontekstu</link>
		<comments>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-4-%e2%80%93-sitemesh-menu-atrybuty-kontekstu#comments</comments>
		<pubDate>Wed, 19 May 2010 19:48:13 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[sitemesh 3]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=898</guid>
		<description><![CDATA[Jak zapowiedziałem w poprzednim poście tym razem pobawimy się Sitemesh-em i atrybutami kontekstowymi w JSP, więc będzie to wpis raczej krótki. Skupimy się na rozdzieleniu szablonów (layoutu) aplikacji na 2 części. Pierwszy szblon zastosujemy do panelu administracyjnego, wyświetlać będzie listy, &#8230; <a href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-4-%e2%80%93-sitemesh-menu-atrybuty-kontekstu">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Jak zapowiedziałem w <a title="Spring Framework 3.0 Tutorial – cz 3 – spring security" href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-–-cz-3-–-spring-security" target="_blank">poprzednim poście</a> tym razem pobawimy się Sitemesh-em i atrybutami kontekstowymi w JSP, więc będzie to wpis raczej krótki. Skupimy się na rozdzieleniu szablonów (layoutu) aplikacji na 2 części. Pierwszy szblon zastosujemy do panelu administracyjnego, wyświetlać będzie listy, formularze i dane, a także umieści na każdej stronie menu administracyjne. Drugi szablon będzie wykorzystywany jedynie podczas logowania i prócz formularza  nie będzie zawierał nic więcej. Oczywiście jeśli ktoś chce może śmiało dodać więcej szablonów, np. specjalna strona dla obsługi zamówień która zawiera oddzielne menu, czy też<span id="more-898"></span></p>
<p>W <a title=" « Spring Framework 3.0 Tutorial – wstęp Spring Framework 3.0 Tutorial – cz 2 – baza danych, walidacja, wiadomości, encje, hibernate » Spring Framework 3.0 Tutorial – cz 1 – przygotowanie projektu, witaj świecie " href="http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie" target="_blank">pierwszej części tutorialu</a> wspomniałem iż Sitemesh służy do składania stron w całość, dzięki niemu możemy stworzyć szablon strony, do którego będziemy doklejać wyniki stron co skutecznie  ułatwia tworzenie widoków. Co prawda biblioteka ta potrafi więcej (w wersji 2.x) ale z racji, że korzystamy z wersji alpha lepiej używać jedynie podstawowych funkcjonalności (przynajmniej w tej chwili).</p>
<h3>Rozdzielamy szablony</h3>
<p>Sitemesh-a skonfigurowaliśmy w pierwszej części tuorialu, miał on za zadanie składać główny szablon (default.jsp) z widokami wyników wygenerowanymi przez aplikację, teraz zajmiemy się rozdzieleniem szablonów dla widoku logowania oraz dla pozostałej części aplikacji. Jak wiadomo, konfigurację Sitemesh-a trzymamy w klasie <strong>com.darekzon.bookstore.filter.Sitemesh</strong>, i aktualnie zawiera wpis mówiący, że każdy wyniki ma być &#8222;wklejony&#8221; do szablony <strong>default.jsp. </strong>Aby utworzyć dodatkowy szablon wystarczy dopisać regułę która dla żądania <strong>/login.html</strong> będzie stosować inny szablon. Całość wygląda następująco:</p>
<pre class="brush: java; title: ; notranslate">
package com.darekzon.bookstore.filter;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
public class Sitemesh extends ConfigurableSiteMeshFilter {
  protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
    builder.addDecoratorPath(&quot;/login.html&quot;, &quot;/views/layout/login.jsp&quot;);
    builder.addDecoratorPath(&quot;/*&quot;, &quot;/views/layout/default.jsp&quot;);
  }
}
</pre>
<p>W powyższym zapisie ważna jest kolejność wpisów (odwrotny zapis przysłaniałby nam szablon <strong>login.jsp </strong>dla wywołania strony logowania.</p>
<p>Teraz wystarczy stworzyć odpowiedni plik (np. bazując na wcześniejszym szablonie) i gotowe, zapytanie o stronę <strong>/login.html</strong> otrzyma własną stronę wynikową.</p>
<h3>Tworzymy menu administracyjne</h3>
<p>Teraz zajmiemy się tworzeniem menu, czynność z pozoru prosta, wystarczy wpisać kilka tagów i gotowe, jak jednak sprawić by nasze menu rozpoznawało gdzie aktualnie się znajdujemy i na bazie tego zaznaczało w menu naszą pozycję? W poprzednim projekcie używałem Sitemesh 2.x który w łatwy sposób odczytywał metatagi stron dzięki czemu łatwo można było określić naszą pozycję, niestety w wersji 3 nie działa to jak trzeba, a na pewno nie jest to w żaden sposób udokumentowane, więc trzeba sobie poradzić w inny sposób. Rozwiązaniem jakie wymyśliłem, jest ustawianie atrybutów strony w zależności od tego gdzie jesteśmy. Jak to działa?</p>
<p>Jak wszyscy wiemy, każda akcja posiada własny plik za pomącą którego generowany jest widok, w każdym z tych plików wstawiamy zapis:</p>
<pre class="brush: java; title: ; notranslate">
&lt;% pageContext.setAttribute(&quot;moduleName&quot;,&quot;content&quot;, PageContext.REQUEST_SCOPE); pageContext.setAttribute(&quot;pageName&quot;,&quot;index&quot;, PageContext.REQUEST_SCOPE); %&gt;</pre>
<p>Powyższy kod, ustawił nam w pliku widoku 2 atrybuty, <strong>moduleName</strong> która oznaczać będzie w jakiej grupie znajduje się strona oraz <strong>pageName</strong> która oznacza jaka strona jest generowana, <strong>PageContext.REQUEST_SCOPE</strong> mówi aplikacji, że atrybuty będą widoczne tylko w zakresie aktualnego żądania (więcej nam nie trzeba).</p>
<p>Zabieramy się za tworzenie menu, ponieważ menu. Możemy je stworzyć w pliku szablonu albo w oddzielnym pliku który dołączymy do naszego szablonu, ponieważ nie lubię chaosu, wybieram rozwiązanie drugie.<br />
Tworzymy plik <strong>navigation.jsp</strong> (w folderze includes) oraz dołączamy go do naszego szablonu <strong>default.jsp</strong> dyrektywą:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;%@ include file=&quot;../includes/navigation.jsp&quot; %&gt;
</pre>
<p>Dołączony plik ma za zadanie odczytać atrybuty ustawione w widokach (patrz wyżej) oraz oznaczenie grupy i podstron klasą <strong>active</strong> gdy są one aktywne, dzięki czemu będziemy mogli za pomącą CSS-a odpowiednio pokazywać i ukrywać nasze menu. Nasze menu zbudowane będzie z zagnieżdżonych list nieuporządkowanych według struktury:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;ul&gt;
	&lt;li&gt;GRUPA
&lt;ul&gt;
	&lt;li&gt;PODSTRONA&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Plik ten prezentuje się następująco:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;%@ taglib prefix=&quot;tag&quot; uri=&quot;http://www.springframework.org/tags&quot; %&gt;
&lt;% String moduleName = (String) pageContext.getAttribute(&quot;moduleName&quot;,PageContext.REQUEST_SCOPE);  String pageName = (String) pageContext.getAttribute(&quot;pageName&quot;,PageContext.REQUEST_SCOPE); %&gt;
&lt;ul&gt;
	&lt;li class=&quot;&lt;% if(moduleName==&quot;&gt;&quot;&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/content.html&quot;&gt;

      &lt;/a&gt;&lt;/li&gt;
	&lt;li class=&quot;&lt;% if(moduleName==&quot;&gt;&quot;&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/catalog.html&quot;&gt;

      &lt;/a&gt;
&lt;ul&gt;
	&lt;li class=&quot;&lt;% if(moduleName==&quot;&gt;&quot;&gt;
          &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/catalog/categories.html&quot;&gt;

          &lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
          &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/catalog/books.html&quot;&gt;

          &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
	&lt;li class=&quot;&lt;% if(moduleName==&quot;&gt;&quot;&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/orders.html&quot;&gt;

      &lt;/a&gt;&lt;/li&gt;
	&lt;li class=&quot;&lt;% if(moduleName==&quot;&gt;&quot;&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/clients.html&quot;&gt;

      &lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/settings.html&quot;&gt;

      &lt;/a&gt;
&lt;ul&gt;
	&lt;li&gt;
          &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/settings/administrators.html&quot;&gt;

          &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
	&lt;li class=&quot;logout&quot;&gt;
      &lt;a href=&quot;&lt;%= request.getContextPath() %&gt;/wyloguj.html&quot;&gt;

      &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Plik dołącza bibliotekę tagów wykorzystywaną do obsługi wiadomości, oraz odczytuje wcześniej ustawione atrybuty zapisując je do zmiennych. W kolejnych pozycjach menu następuje sprawdzenie aktualnej pozycji.<br />
Gdy <strong>moduleName</strong> jest równe wymaganej wartości zostaje ustawiona klasa <strong>active</strong>, to samo tyczy się zmiennej <strong>pageName. </strong>Dzięki takiemu rozwiązaniu mamy nasze &#8222;dynamiczne&#8221; menu.</p>
<p>Jeśli ktoś zna lepsze rozwiązanie z chęcią je poznam.</p>
<p>PS. Jeśli chodzi o zapis <strong>request.getContextPath()</strong> to zwraca nam on ścieżkę wywołania aplikacji, tzn. jeśli nasza aplikacja działała by w folderze <strong>bookstore </strong>a jej uruchomienie wiązałoby się z wpisanie adresu <strong>http://example.com/bookstore/</strong> to <strong>getContextPath()</strong> zwróci nam ciąg <strong>/bookstore</strong>, dzięki czemu wszystkie linki będą poprawnie obsłużone bez względu na to, czy nasza aplikacja jest uruchomiona na własnej domenie, czy też działa &#8222;w folderze&#8221;.</p>
<h3>Jak pobrać projekt</h3>
<blockquote><p>Projekt został umieszczony na serwerach <a title="GitHub - social coding" href="http://github.com/" target="_blank">GitHub.com</a>. Adres bezpośredni do projektu: <a title="Bookstore" href="https://github.com/darek/bookstore" target="_blank">https://github.com/darek/bookstore</a>.<br />
Dodatkowo zostało uruchomione repozytorium <strong>GIT</strong> dla wszystkich chętnych i dostępny jest pod adresem: <a title="Repozutorium projektu Bookstore" href="git://github.com/darek/bookstore.git" target="_blank">git://github.com/darek/bookstore.git</a></p></blockquote>
<div class="shr-publisher-898"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-4-%e2%80%93-sitemesh-menu-atrybuty-kontekstu/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Spring Framework 3.0 Tutorial – cz 3 – spring security</title>
		<link>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-3-%e2%80%93-spring-security</link>
		<comments>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-3-%e2%80%93-spring-security#comments</comments>
		<pubDate>Tue, 18 May 2010 15:02:11 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[javaee]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[spring security]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=819</guid>
		<description><![CDATA[W drugiej części tutorialu udało nam się stworzyć mechanizm dodawania administratorów do naszego panelu, byłoby nierozsądne by każdy użytkownik miał do niego dostęp, dlatego w tej części zajmiemy się mechanizmem kontroli dostępu do naszej aplikacji. Wpis obejmie konfigurację mechanizmów uwierzytelniania &#8230; <a href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-3-%e2%80%93-spring-security">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>W <a title="Spring Framework 3.0 Tutorial – cz 2 – baza danych, walidacja, wiadomości, encje, hibernate" href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-–-cz-2-–-baza-danych-walidacja-wiadomosci-encje-hibernate" target="_blank">drugiej części</a> tutorialu udało nam się stworzyć mechanizm dodawania administratorów do naszego panelu, byłoby nierozsądne by każdy użytkownik miał do niego dostęp, dlatego w tej części zajmiemy się mechanizmem kontroli dostępu do naszej aplikacji. Wpis obejmie konfigurację mechanizmów uwierzytelniania oraz autoryzacji wykorzystujących Spring Security (w tym hasła użytkowników zakodowane algorytmem sha256 + z wykorzystaniem tzw. soli). Miało być też coś o Sitemeshu, ale zrobię to w następnym odcinku który pojawi się na dniach.<span id="more-819"></span></p>
<h3>Zabezpieczanie aplikacji</h3>
<p>Konfiguracja zabezpieczeń obejmuje dwa pliki, <strong>web-security.xml</strong> oraz <strong>web.xml. </strong>Pierwszy z nich zawiera już konfigurację obiektu szyfrującego hasła oraz mechanizm soli (<a title="Spring Framework 3.0 Tutorial – cz 2 – baza danych, walidacja, wiadomości, encje, hibernate" href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%E2%80%93-cz-2-%E2%80%93-baza-danych-walidacja-wiadomosci-encje-hibernate">z wcześniejszego tutorialu</a>), tym razem musimy dodać obsługę poziomów dostępu oraz mechanizmu uwierzytelniania opartego o bazę danych. Konfiguracja taka prócz skonfigurowanych już wcześniej obiektów zawierać powinna manager uwierzytelniania, dostawcę danych uwierzytelnianych, filtr przechwytujący, oraz listę reguł które będą mówić mechanizmowi, kto gdzie ma dostęp i ew. gdzie ma się zalogować. Zmiany w tym pliku wyglądają następująco<em> </em></p>
<p><em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;authentication-manager alias=&quot;authenticationManager&quot;&gt;
&lt;authentication-provider ref=&quot;authProvider&quot; /&gt;
&lt;/authentication-manager&gt;

&lt;beans:bean id=&quot;authProvider&quot;
class=&quot;org.springframework.security.authentication.dao.DaoAuthenticationProvider&quot;&gt;
&lt;beans:property name=&quot;userDetailsService&quot; ref=&quot;userService&quot; /&gt;
&lt;beans:property name=&quot;passwordEncoder&quot; ref=&quot;passwordEncoder&quot; /&gt;
&lt;beans:property name=&quot;saltSource&quot; ref=&quot;saltSource&quot; /&gt;
&lt;beans:property name=&quot;includeDetailsObject&quot; value=&quot;true&quot; /&gt;
&lt;/beans:bean&gt;

&lt;beans:bean id=&quot;authenticationProcessingFilterEntryPoint&quot;
class=&quot;org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint&quot;&gt;
&lt;beans:property name=&quot;forceHttps&quot; value=&quot;false&quot; /&gt;
&lt;beans:property name=&quot;loginFormUrl&quot; value=&quot;/login.html&quot; /&gt;
&lt;/beans:bean&gt;

&lt;http entry-point-ref=&quot;authenticationProcessingFilterEntryPoint&quot; auto-config=&quot;true&quot;&gt;
&lt;!-- &lt;intercept-url pattern=&quot;/**&quot; access=&quot;ROLE_ADMIN&quot; /&gt; --&gt;
&lt;intercept-url pattern=&quot;/login.html&quot; filters=&quot;none&quot; /&gt;
&lt;logout logout-url=&quot;/logout.html&quot; /&gt;
&lt;anonymous granted-authority=&quot;ROLE_ANONYMOUS&quot; /&gt;
&lt;form-login login-page=&quot;/login.html&quot; login-processing-url=&quot;/j_spring_security_check.html&quot;
authentication-failure-url=&quot;/login.html?error=true&quot; /&gt;
&lt;/http&gt;
</pre>
<p>Czyli:</p>
<p><strong>authentication-manager</strong>: mechanizm odpowiedzialny za uwierzytelnianie, przekazujemy mu referencje do obiektu odpowiedzialnego za dostarczenie informacji o użytkownikach</p>
<p><strong>authProvider</strong>: obiekt odpowiedzialny jest za dostarczenie użytkowników (tj, pobranie użytkownika na podstawie loginu), w naszym wypadku obiekt korzysta z bazy danych oraz dodatkowych obiektów szyfrujących. authProvider posiada również parametr <strong>userDetailsService </strong>który powinien wskazywać na obiekt odpowiedzialny za wyszukanie użytkownika, obiekt taki wskazujemy poprzez nadanie mu id <strong>userService</strong> (patrz web-data.xml).</p>
<p><strong>authenticationProcessingFilterEntryPoint</strong>: obiekt rozpoczynający proces autoryzacji, przechowuje ścieżkę (w formie URL-a) do strony z formularzem logowania</p>
<p><strong>http: </strong>najważniejsza część, tutaj ustawiamy poziomy dostępów do konkretnych zasobów, ustalamy również formularza logowania oraz adres powodujący wylogowanie. (<strong>UWAGA!</strong> jeśli ustalamy niestandardowy adres logowania należy wyłączyć mu poziom dostepu (<em>filter=&#8221;none&#8221;</em>) inaczej dostaniemy pętle przekierowań)</p>
<p>Według powyższej konfiguracji nasza aplikacja jest dostępna tylko dla użytkowników z poziomem dostępu <strong>ROLE_ADMIN</strong>, jeśli nie jesteś zalogowany zostajesz automatycznie przekierowany na stronę <strong>/login.html</strong> która wyświetla formularz i wysyła go pod adres <strong>/j_spring_security_check.html</strong>, jeśli logowanie się nie powiedzie, do adresu logowania zostanie dopisany parametr <strong>error=true, </strong>gdy natomiast podasz poprawne dane logujące zostaniesz przeniesiony na wcześniej żądaną stronę, lub na stronę główną serwisu. Aby się wylogować musisz wejść pod adres <strong>/logout.html</strong>.</p>
<p>W pliku <strong>web.xml</strong> ustawiamy filtr który każde żądanie kończące się rozszerzeniem <strong>.html</strong> będzie &#8222;testował&#8221; pod względem bezpieczeństwa (tzn. czy użytkownik wywołujący żądanie ma prawo je wykonać), zdefiniowany filtr wygląda tak <em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;filter&gt;
&lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
&lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
&lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
</pre>
<p>Jak wspomniałem wcześniej gdy użytkownik nie ma praw do wykonania danego żądania, bądź nie jest zalogowany zostaje automatycznie przekierowany do strony <strong>loguj.html</strong>. Ponieważ nie jest to standardowa strona musimy sami obsłużyć to żądanie, na szczęście nie jest to trudne, wymaga tylko 2 elementów, metody w kontrolerze która obsłuży żądanie oraz widoku formularza który wyświetli pola do podania loginu i hasła. Akcja w kontrolerze dodatkowo przyjmie parametr <strong>error </strong>(o czym mówiłem wcześniej) który posłuży do wykrycia czy akcja logowania się nie powiodła i jeśli jest to prawdą wyświetli odpowiedni komunikat.</p>
<p>Metodę obsługującą logowanie zawarłem w kontrolerze <strong>IndexController</strong> i wygląda ona następująco <em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: java; title: ; notranslate">
@RequestMapping(value = &quot;/login&quot;, method = RequestMethod.GET)
 public ModelAndView login(@RequestParam(value = &quot;error&quot;, required = false, defaultValue = &quot;false&quot;) boolean error) {
 ModelAndView mav = new ModelAndView(&quot;index/login&quot;);
 mav.addObject(&quot;isError&quot;, error);
 return mav;
 }
</pre>
<p>@<strong>RequestParam</strong> pobiera parametr przekazywany w żądaniu i przekazuje go w zmiennej error, parametr ten nie jest wymagany, więc jeśli nie istnieje do zmiennej <strong>error</strong> zostanie przypisana wartość domyślna <strong>false.</strong> Parametr <strong>error</strong> przekazywany jest do widoku który prezentuje się następująco:</p>
<pre class="brush: xml; title: ; notranslate">

&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jstl/core_rt&quot; %&gt;
&lt;%@ taglib prefix=&quot;tag&quot; uri=&quot;http://www.springframework.org/tags&quot; %&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;
&lt;form action=&quot;&lt;c:url value=&quot;&gt;
&quot; method=&quot;POST&quot;&gt;
&lt;div&gt;
 &lt;label for=&quot;login&quot;&gt;&lt;/label&gt;
 &lt;input id=&quot;login&quot; name=&quot;j_username&quot; /&gt;&lt;/div&gt;
&lt;div&gt;
 &lt;label for=&quot;password&quot;&gt;&lt;/label&gt;
 &lt;input id=&quot;password&quot; name=&quot;j_password&quot; type=&quot;password&quot; /&gt;&lt;/div&gt;
&lt;div&gt;
 &lt;input type=&quot;submit&quot; /&gt;&lt;/div&gt;
&lt;/form&gt;&lt;/div&gt;
</pre>
<p>Jak widzimy powyżej, najpierw sprawdzamy czy zmienna <strong>isError</strong> jest równa <strong>true</strong> co by oznaczało, że podaliśmy błędne dane podczas logowania, jeśli warunek jest spełnony zostaje wyświetlony komunikat. Następnie tworzymy formularz który metodą POST przesyła dane pod adres <strong>/j_spring_security_check.html</strong> który jak pamiętacie ustawiliśmy wcześniej w pliku <strong>web-security.xml</strong>. Formularz musi zawierać przynajmniej dwa pola (login i hasło) które dodatkowo powinny przyjmować odpowiednie nazwy: <strong>j_username</strong> dla nazwy użytkownika oraz <strong>j_password</strong> dla jego hasła.</p>
<p>Byłoby wspaniale gdyby na tym zakończyła się konfiguracja, niestety musimy zmodyfikować klasę encji <strong>Account</strong> by mogła być poprawnie przetwarzana przez Spring Security. Aby nasza klasa była poprawnie obsługiwana musi rozszerzać klasę <strong>User</strong> (z pakietu org.springframework.security.core.userdetails) oraz implementować interfejs <strong>UserDetails</strong> (z tego samego pakietu).<br />
W naszym wypadku implementacja wygląda następująco <em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: java; title: ; notranslate">

public class Account extends User implements UserDetails {

@Override
 public Collection getAuthorities() {
 Set ga = new HashSet();
 for (AccountRole ar : this.accountRole) {
 ga.add(new GrantedAuthorityImpl(ar.getRole()));
 }
 return ga;
 }
&lt;pre&gt; @Override
 public boolean isEnabled() {
 if (this.accountRole.size() &gt; 0) {
 return true;
 }
 return false;
 }&lt;/pre&gt;
@Override
 public boolean isAccountNonExpired() {
 return this.isEnabled();
 }

 @Override
 public boolean isAccountNonLocked() {
 return this.isEnabled();
 }

 @Override
 public boolean isCredentialsNonExpired() {
 return this.isEnabled();
 }

}
</pre>
<p>Zostały zaimplementowane metody:</p>
<p><strong>public Collection&lt;GrantedAuthority&gt; getAuthorities()</strong> &#8211; zwraca listę poziomów dostępu jakie posiada użytkownik, czyli nasze <strong>AccountRole,</strong><br />
<strong>public boolean isEnabled()</strong> &#8211; zwraca <strong>true</strong> jeśli konto jest aktywne, w naszym wypadku kierujemy się zasadą &#8222;Jeśli użytkownik ma przypisaną rolę to konto jest aktywne&#8221;,<br />
<strong>public boolean isAccountNonExpired() &#8211; </strong>zwraca <strong>true</strong> jeśli konto nie wygasło,<br />
<strong>public boolean isAccountNonLocked()</strong> &#8211; zwraca <strong>true</strong> jeśli konto nie jest zablokowane,<br />
<strong>public boolean isCredentialsNonExpired()</strong> &#8211; zwraca <strong>true</strong> jeśli poziomy dostępów nie wygasły</p>
<p>Musimy również dodać metodę <strong>loadUserByUsername</strong> która będzie wykorzystywana przez dostawcę autoryzacji do znalezienia użytkownika. Funkcja ta wykorzystuje metodę <strong>findUsername</strong> z klasy <strong>AccountDao</strong> którą wykorzystujemy podczas rejestracji, i prezentuje się ona następująco <em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: java; title: ; notranslate">

@Override
 public UserDetails loadUserByUsername(String username)
 throws UsernameNotFoundException, DataAccessException {
 try {
 return (UserDetails) accountDao.findUsername(username);
 } catch (UserNotFoundException e) {
 throw new UsernameNotFoundException(username);
 }
 }
</pre>
<p>Metodę tą dodajemy do obiektu który jest wskazany w obiekcie <strong>authProvider</strong> jako parametr <strong>userDetailsService</strong>, w naszym wypadku było to dodanie identyfikatora <strong>userService</strong> do obiektu <strong>AccountServiceImpl</strong> w pliku <strong>web-data.xml</strong> <em>(pełna zawartość pliku w repozytorium – zobacz koniec wpisu)</em>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;bean id=&quot;userService&quot; class=&quot;com.darekzon.bookstore.service.AccountServiceImpl&quot; /&gt;
</pre>
<p>I to wszystko, tak skonfigurowany mechanizm powinien działać poprawnie, pamiętajcie tylko, żeby włączyć zabezpieczenia po dodaniu użytkownika.</p>
<h3>Jak pobrać projekt</h3>
<blockquote><p>Projekt został umieszczony na serwerach <a title="GitHub - social coding" href="http://github.com/" target="_blank">GitHub.com</a>. Adres bezpośredni do projektu: <a title="Bookstore" href="https://github.com/darek/bookstore" target="_blank">https://github.com/darek/bookstore</a>.<br />
Dodatkowo zostało uruchomione repozytorium <strong>GIT</strong> dla wszystkich chętnych i dostępny jest pod adresem: <a title="Repozutorium projektu Bookstore" href="git://github.com/darek/bookstore.git" target="_blank">git://github.com/darek/bookstore.git</a></p></blockquote>
<div class="shr-publisher-819"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-3-%e2%80%93-spring-security/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Spring Framework 3.0 Tutorial – cz 2 – baza danych, walidacja, wiadomości, encje, hibernate</title>
		<link>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-2-%e2%80%93-baza-danych-walidacja-wiadomosci-encje-hibernate</link>
		<comments>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-2-%e2%80%93-baza-danych-walidacja-wiadomosci-encje-hibernate#comments</comments>
		<pubDate>Thu, 06 May 2010 08:48:27 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[javaee]]></category>
		<category><![CDATA[jsr 303]]></category>
		<category><![CDATA[persistence]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=736</guid>
		<description><![CDATA[W tej części tutorialu skupimy się na skonfigurowaniu połączenia z bazą danych, podłączeniu frameworka hibernate do naszej aplikacji oraz zobaczymy jak tworzyć encje i jak sprawdzać poprawność danych przed ich zapisem (walidacja). Przygotowanie do pracy W poprzedniej części przygotowaliśmy w &#8230; <a href="http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-2-%e2%80%93-baza-danych-walidacja-wiadomosci-encje-hibernate">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>W tej części tutorialu skupimy się na skonfigurowaniu połączenia z bazą danych, podłączeniu frameworka hibernate do naszej aplikacji oraz zobaczymy jak tworzyć encje i jak sprawdzać poprawność danych przed ich zapisem (walidacja).<span id="more-736"></span></p>
<h2>Przygotowanie do pracy</h2>
<p>W poprzedniej części przygotowaliśmy w ramach części klienckiej małą aplikację &#8222;witaj świecie&#8221;, ponieważ projekt nie zawierał elementów stricte przyporządkowanych części dla klienta możemy skopiować go (głównie pliki konfiguracyjne) i użyć w panelu administracyjnym (prawie jak <a href="http://pl.wikipedia.org/wiki/Antywzorzec_projektowy#Antywzorce_metodologiczne" target="_blank">copypasteryzm</a>).</p>
<p>Aby przejść przez tą część tutoriala będziemy musieli zainstalować bazę danych <a href="http://www.postgresql.org/" target="_blank">PostgreSQL</a> oraz pobrać kilka dodatkowy bibliotek (zrobimy to wykorzystując mavena).<br />
Jak wspomniałem w pierwszym odcinku tutoriala, wspólne klasy będą trzymane w osobnym projekcie &#8222;<strong>bookstore-lib&#8221;</strong>, tam przechowywane będą klasy Encji, Dao oraz Serwisy, a także wspólne klasy które będą wykonywać specyficzne zadania (powstaną wkrótce), eclipse zadba, by dołączyć je automatycznie podczas kompilacji. Projekt &#8222;bookstore-lib&#8221; również korzysta z mavena do obsługi bibliotek wykorzystanych w projekcie, nie posiada on natomiast zintegrowanego Spring frameworka (co jest logiczne). Nad jego konfiguracją rozpisywać się nie będę, stworzyłem czysty projekt, dodałem obsługę mavena i zacząłem pisać klasy (w tym przypadku na pierwszy ogień poszły Encje), gdy eclipse wykrył, że nie mam jakiejś klasy zależnej dodałem ją do projektu ustawiając odpowiedni zakres (<a href="http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope" target="_blank">scope</a> &#8211; jeśli klasa wykorzystywana jest w panelu, zakres ustawiałem na runtime, jeśli była używana stricte w <strong>bookstore-lib</strong>, zakres ustawiany był na compile).</p>
<p>Projekt &#8222;<strong>bookstore-admin</strong>&#8221; prócz bibliotek potrzebnych do uruchomienia Springframework-a potrzebuje również bibliotek odpowiedzialnych za obsługę bazy danych oraz walidację formularzy, do pliku <strong>pom.xml</strong> dodałem następujące biblioteki (tu również skorzystałem z zakresów &#8211; scope, dzięki czemu aplikacje nie posiadały zdublowanych bibliotek).</p>
<pre class="brush: xml; title: ; notranslate">
&lt;dependency&gt;
 &lt;groupId&gt;postgresql&lt;/groupId&gt;
 &lt;artifactId&gt;postgresql&lt;/artifactId&gt;
 &lt;version&gt;8.4-701.jdbc4&lt;/version&gt;
 &lt;type&gt;jar&lt;/type&gt;
 &lt;scope&gt;compile&lt;/scope&gt;
 &lt;/dependency&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.springframework&lt;/groupId&gt;
 &lt;artifactId&gt;spring-orm&lt;/artifactId&gt;
 &lt;version&gt;3.0.2.RELEASE&lt;/version&gt;
 &lt;type&gt;jar&lt;/type&gt;
 &lt;scope&gt;compile&lt;/scope&gt;
 &lt;/dependency&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;artifactId&gt;hibernate-core&lt;/artifactId&gt;
 &lt;version&gt;3.3.2.GA&lt;/version&gt;
 &lt;type&gt;jar&lt;/type&gt;
 &lt;scope&gt;compile&lt;/scope&gt;
 &lt;/dependency&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;artifactId&gt;hibernate-entitymanager&lt;/artifactId&gt;
 &lt;version&gt;3.4.0.GA&lt;/version&gt;
 &lt;type&gt;jar&lt;/type&gt;
 &lt;scope&gt;compile&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.5.8&lt;/version&gt;
 &lt;type&gt;jar&lt;/type&gt;
 &lt;scope&gt;compile&lt;/scope&gt;
 &lt;/dependency&gt;
&lt;dependency&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
 &lt;version&gt;4.0.2.GA&lt;/version&gt;
 &lt;/dependency&gt;
</pre>
<h2>Konfiguracja bazy danych</h2>
<p>Konfiguracja bazy danych jak i wszystkich elementów związanych z obsługą zapisu do bazy danych oraz odczytu z niej znajduje się w pliku <strong>web-data.xml</strong> (trzeba być porządnym programistą i nie tworzyć śmietnika konfiguracyjnego).<br />
Zanim skonfigurujemy połączenie z bazą danych musimy stworzyć użytkownika bazy danych oraz dodać bazę z którą będzie się łączyć, jeśli ktoś nie wie polecam <a href="http://google.com" target="_blank">google.com</a> lub program <a href="http://www.pgadmin.org/" target="_blank">pgAdmin</a>. Połączenie z bazą danych możemy skonfigurować na 2 sposoby (tyle znam), pierwszy (standardowy), czyli dostęp do bazy poprzez adres serwera+port oraz dane uwierzytelniające, drugi to dostęp do bazy poprzez nazwę JNDI (czytaj więcej o <a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Resources2.html" target="_blank">Java Naming and Directory Interface API</a>), ważną zaletą (moim zdaniem) połączenia JNDI jest niezależność ustawień autoryzacyjnych, ponieważ łączymy się z bazą nie poprzez login i hasło, a ustaloną wcześniej przez serwer nazwę, możliwe jest bezproblemowe (bez potrzeby ponownego grzebania w konfiguracji) zmienianie danych autoryzacyjnych, czy też adresu bazy danych dlatego też oprę naszą aplikację o ten typ połączenia.<br />
Ponieważ połączenia JNDI opierają się na skonfigurowanej nazwie musimy taką konfigurację wprowadzić. W naszym przypadku (tomcat 6.0) robimy to w pliku <strong>TOMCAT_HOME/conf/context.xml</strong><sup>1</sup> poprzez wpisanie między tagi &lt;context&gt; zawartości:</p>
<pre class="brush: xml; title: ; notranslate">&lt;Resource auth=&quot;Container&quot; driverClassName=&quot;org.postgresql.Driver&quot; name=&quot;bookstore&quot; password=&quot;bookstore&quot; type=&quot;javax.sql.DataSource&quot; url=&quot;jdbc:postgresql://localhost:5432/bookstore&quot; username=&quot;bookstore&quot;/&gt;</pre>
<p>Co w skrócie oznacza &#8222;<em>przypisz nazwie <strong>bookstore</strong> połączenie do bazy o nazwie <strong>bookstore</strong> znajdującej się pod adresem <strong>localhost</strong>, na porcie <strong>5432</strong> wykorzystując nazwę użytkownika <strong>bookstore</strong> oraz hasło <strong>bookstore</strong>&#8222;.</em></p>
<p>Nasza aplikacja korzystać będzie z frameworka hibernate, dlatego potrzebujemy skonfigurować jego działanie. Konfiguracja taka polega na utworzeniu pliku  <strong>hibernate.cfg.xml</strong> w  <strong>classpath</strong> (np. src/META-INF; może ktoś zna polski odpowiednik classpath) i wpisujemy zawartość:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE hibernate-configuration PUBLIC
 &quot;-//Hibernate/Hibernate Configuration DTD 3.0//EN&quot;
 &quot;http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd&quot;&gt;
&lt;hibernate-configuration&gt;
 &lt;session-factory&gt;
 &lt;property name=&quot;hibernate.dialect&quot;&gt;org.hibernate.dialect.PostgreSQLDialect&lt;/property&gt;
 &lt;property name=&quot;hibernate.hbm2ddl.auto&quot;&gt;update&lt;/property&gt;
 &lt;property name=&quot;hibernate.show_sql&quot;&gt;true&lt;/property&gt;
 &lt;property name=&quot;hibernate.format_sql&quot;&gt;true&lt;/property&gt;
 &lt;property name=&quot;hibernate.use_sql_comments&quot;&gt;true&lt;/property&gt;
 &lt;mapping
 class=&quot;com.darekzon.bookstore.domain.Account&quot; /&gt;
 &lt;mapping
 class=&quot;com.darekzon.bookstore.domain.AccountRole&quot; /&gt;
 &lt;mapping
 class=&quot;com.darekzon.bookstore.domain.Administrator&quot; /&gt;
 &lt;/session-factory&gt;
&lt;/hibernate-configuration&gt;
</pre>
<p>Pliik ustawia kolejno:</p>
<ol>
<li>hibernate.dialect -  jaki język zapytań będzie wykorzystywany (w tym wypadku rozszerzenia dodane przez postgresql)</li>
<li>hibernate.hbm2ddl.auto &#8211; automatycznie aktualizuj schemat bazy danych na podstawie class encji (super sprawa)</li>
<li>hibernate.show_sql &#8211; pokazuje zapytania sql jakie wykonuje hibernate (dobre przy debugowaniu, czasem wprowadza chaos do konsoli)</li>
<li>hibernate.format_sql &#8211; formatuje w/w zapytania, przydaje się</li>
<li>hibernate.user_sql_comments &#8211; dodatkowe komentarze, pomagają zorientować się jakie zapytanie aktualnie jest wykonywane</li>
<li>mapping class  &#8211; wskazanie jakie klasy traktowane są jako klasy encji (muszą być oznaczone adnotacją <strong>@Entity</strong>), wykorzystywane przy aktualizacji schematów</li>
</ol>
<p>Drugim ważnym plikiem jest <strong>persistence.xml</strong> który konfiguruje nam PersistenceUnit czyli jednostę utrwalającą. Plik ten ma zawartość:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;persistence xmlns=&quot;http://java.sun.com/xml/ns/persistence&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd&quot;
 version=&quot;2.0&quot;&gt;
 &lt;persistence-unit name=&quot;bookstorePU&quot; transaction-type=&quot;RESOURCE_LOCAL&quot;&gt;
 &lt;provider&gt;org.hibernate.ejb.HibernatePersistence&lt;/provider&gt;
 &lt;jta-data-source&gt;bookstore&lt;/jta-data-source&gt;
 &lt;class&gt;com.darekzon.bookstore.domain.Administrator&lt;/class&gt;
 &lt;class&gt;com.darekzon.bookstore.domain.Account&lt;/class&gt;
 &lt;class&gt;com.darekzon.bookstore.domain.AccountRole&lt;/class&gt;
 &lt;/persistence-unit&gt;
&lt;/persistence&gt;
</pre>
<p>Określamy w nim nazwę jednostki utrwalającej, <a href="http://openejb.apache.org/3.0/jpa-concepts.html" target="_blank">typ transakcji</a>, dostawę jednostki oraz adres naszego źródła danych, musimy również wprowadzić informacje, które klasy będą używane podczas utrwalania (więc pewne dane się zdublują z plikiem hibernate.cfg.xml). Powyższy plik możemy umieścić w tym samym miejscu co plik <strong>hibernate.cfg.xml</strong>, tak by był dostępny dla JVM podczas ładowania aplikacji.</p>
<p>Gdy mamy już skonfigurowanego hibernate-a oraz jednostę utrwalającą, pora zintegrować je z naszą aplikacja, polegać to będzie na utworzeniu szeregu <strong>beanów</strong> odpowiedzialnych za obsługę żądań do bazy danych. Konfiguracja ta została zapisana w pliku <strong>web-data.xml</strong> i ma zawartość:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
 xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot; xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
 xmlns:jee=&quot;http://www.springframework.org/schema/jee&quot;
 xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd&quot;&gt;

 &lt;jee:jndi-lookup id=&quot;dataSource&quot; jndi-name=&quot;java:comp/env/bookstore&quot;
 cache=&quot;true&quot; resource-ref=&quot;true&quot; lookup-on-startup=&quot;false&quot;
 proxy-interface=&quot;javax.sql.DataSource&quot; /&gt;

&lt;bean id=&quot;entityManagerFactory&quot;
 class=&quot;org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean&quot;&gt;
 &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;
 &lt;/bean&gt;

 &lt;bean id=&quot;sessionFactory&quot;
 class=&quot;org.springframework.orm.hibernate3.LocalSessionFactoryBean&quot;&gt;
 &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;
 &lt;property name=&quot;configurationClass&quot; value=&quot;org.hibernate.cfg.AnnotationConfiguration&quot;/&gt;
 &lt;property name=&quot;configLocation&quot; value=&quot;classpath:META-INF/hibernate.cfg.xml&quot; /&gt;
 &lt;/bean&gt;

 &lt;bean id=&quot;transactionManager&quot;
 class=&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;&gt;
 &lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot; /&gt;
 &lt;/bean&gt;

 &lt;bean id=&quot;templateManager&quot;
class=&quot;org.springframework.orm.hibernate3.HibernateTemplate&quot;&gt;
 &lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot; /&gt;
 &lt;/bean&gt;

&lt;tx:annotation-driven transaction-manager=&quot;transactionManager&quot; /&gt;

&lt;/beans&gt;
</pre>
<p>Konfiguracja zawiera:</p>
<p><strong>dataSource:<br />
</strong>Wyszukuje nasze połączenie (skonfigurowane w serwerze) i binduje je do nazwy dataSource</p>
<p><strong>entityManager: </strong> manager encji</p>
<p><strong>sessionFactory: </strong>można powiedzieć, że sesja jest tożsama (oznacza to samo) co połączenie z bazą danych. Dzięki podaniu klasy<strong> AnnotationConfiguration</strong> do parametru <em>configurationClass</em> mogę używać adnotacji do konfigurowania encji zamiast pisać pliki <strong>*.hba.xml.</strong> Parametr <strong>configLocation</strong> wskazuje miejsce gdzie znajduje się plik konfiguracyjny hibernate-a.</p>
<p><strong>transactionManager: </strong> manager transakcji (raczej jasne)</p>
<p><strong>templateManager: </strong>posiada szereg metod upraszczających obsługę ORM (wyszukiwanie, zapisywanie, aktualizacja, kasowanie etc.), do działania wymaga sesji podawanej w parametrze <strong>sessionFactory.</strong></p>
<p><strong>tx:annotation-driven: </strong>dzięki temu zapisowi możemy używać dodatkowych adnotacji (np. @Transactional)</p>
<p>Należy pamiętać by dopisać ścieżkę do konfiguracji  (/WEB-INF/web-data.xml) w pliku <strong>web.xml</strong> (w miejscu konfiguracji kontekstu).</p>
<p>Tak skonfigurowany spring, będzie lączył się przy starcie ze zdefiniowanym serwerem bazy danych, udostępnia również szereg obiektów pomagających w dostępie do bazy. Jeśli połączenie działa poprawnie (czyt. przy starcie nie wyskakuje żadne wyjątek) bierzemy się do dalszej pracy.</p>
<h2>Obsługa wiadomości / tłumaczenia</h2>
<p>Jestem zwolennikiem odcinania treści od formy, nie lubię gdy w kodzie strony widzę treści takie jak, nazwy pól, komunikaty błędów, czy też nagłówki. Takie podejście sprawia problem, gdy będziemy musieli zmienić tekst znajdujący się w wielu elementach aplikacji czy też co ważniejsze udostępnić aplikację w wielu językach.  Na szczęście istnieje proste rozwiązanie jakim jest mechanizm wiadomości. Mechanizm ten składa się z 3 elementów: konfiguracji wskazującej gdzie znajdują się teksty, pliku z tekstami oraz odpowiednich wstawek w kodzie aplikacji które wskazują jaka treść ma zostać umieszczona w danym miejscu.</p>
<p>Konfiguracja mechanizmu jest niezwykle prosta, polega na zdefiniowaniu nowego obiektu i przekazaniu mu listy plików w której znajdują się nasze wiadomości (poniżej):</p>
<pre class="brush: xml; title: ; notranslate">

&lt;bean id=&quot;messageSource&quot;
class=&quot;org.springframework.context.support.ResourceBundleMessageSource&quot;&gt;
 &lt;property name=&quot;basenames&quot;&gt;
 &lt;list&gt;
 &lt;value&gt;/WEB-INF/messages/main&lt;/value&gt;
 &lt;/list&gt;
 &lt;/property&gt;
 &lt;/bean&gt;
</pre>
<p>Pewną nieścisłością jest fakt, że obiekt ten będzie przeszukiwał katalog <strong>/WEB-INF/messages/ </strong>w poszukiwaniu pliku <strong>main.properties</strong>, a nie jak można by sadzić pliku <strong>main</strong>, o czym trzeba pamiętać.</p>
<p>Nasz plik z wiadomościami ma prostą budowę, składa się z zestawu   <strong>klucz = treść,</strong> gdzie klucz jest unikalnym identyfikatorem nadawanym naszej treści, będzie on również używany w kodzie strony, gdzie należy wstawiać tagi w postaci <strong>&lt;tag:message code=&#8221;IDENTYFIKATOR&#8221; /&gt;</strong> który zostanie zastąpiony odpowiednią treścią. Proste w wygodne.</p>
<h2><strong>Dodawanie administratorów</strong></h2>
<p>Pierwszym zadaniem z jakim się zmierzymy jest dodawanie administratorów (w następnej części tutoriala zabezpieczymy nasz system, więc potrzebujemy konta by móc się zalogować), gdzie konta powinny spełniać wymagania:</p>
<ol>
<li>hasło musi mieć przynajmniej 8 znaków, ale nie więcej jak 20 znaków</li>
<li>hasła zaszyfrowane zostaną algorytmem SHA-2 (odmiana sha256)</li>
<li>wykorzystana zostanie sól w postaci identyfikatora użytkownika (identyfikator nie będzie wystawiany na widok publiczny)</li>
<li>w bazie danych przechowywani będą użytkownicy o różnych poziomach dostępu, do panelu administracyjnego dostęp będą mieli tylko i wyłącznie Ci użytkownicy, którzy posiadają poziom <strong>ROLE_ADMIN</strong></li>
</ol>
<p>Z powyższych krótkich wymagań można łatwo wywnioskować, że potrzebujemy obiektu który będzie szyfrować ciągi znaków, oraz jednego do tworzenia soli. Obiekty te skonfigurowane zostaną w kolejnym pliku xml zapisanym pod nazwą <strong>security.xml</strong>, z racji, że są to obiekty wykorzystywane przy funkcjach bezpieczeństwa.<br />
Plik ten aktualnie prezentuje się jak poniżej:</p>
<pre class="brush: xml; title: ; notranslate">

&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
 xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot; xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
 xmlns:jee=&quot;http://www.springframework.org/schema/jee&quot;
 xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/jee

http://www.springframework.org/schema/jee/spring-jee-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

http://www.springframework.org/schema/mvc

http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd&quot;&gt;

 &lt;bean id=&quot;passwordEncoder&quot; class=&quot;org.springframework.security.authentication.encoding.ShaPasswordEncoder&quot;&gt;
     &lt;constructor-arg value=&quot;256&quot;/&gt;
 &lt;/bean&gt;

 &lt;bean id=&quot;saltSource&quot; class=&quot;org.springframework.security.authentication.dao.ReflectionSaltSource&quot;&gt;
     &lt;property name=&quot;userPropertyToUse&quot; value=&quot;id&quot;/&gt;
 &lt;/bean&gt;

&lt;/beans&gt;
</pre>
<p>Diagram klas struktury przechowującej dane nt. administratorów wygląda następująco:</p>
<div id="attachment_801" class="wp-caption aligncenter" style="width: 310px"><a href="http://darekzon.com/wp-content/uploads/2010/05/adm_acc_acr_dia.png" rel="lightbox[736]"><img class="size-medium wp-image-801" title="Diagram klas - konta użytkowników" src="http://www1.darekzon.com/wp-content/uploads/2010/05/adm_acc_acr_dia-300x113.png" alt="Diagram klas - konta użytkowników" width="300" height="113" /></a><p class="wp-caption-text">Diagram klas - konta użytkowników</p></div>
<p><a href="http://darekzon.com/wp-content/uploads/2010/04/adm_acc_acr_dia.png" rel="lightbox[736]"><br />
</a>Klasa administratora (na razie pusta) rozszerza klasę użytkownika która posiada login oraz hasło jako dane uwierzytelniające,<strong> </strong>dodatkowo użytkownik agreguje poziom dostępu użytkownika (z racji, że w tej samej tabeli przechowywani będą administratorzy, operatorzy oraz zwykli użytkownicy) który wydelegowany jest do oddzielnej klasy.</p>
<h3>Tworzymy Encje</h3>
<p>Powyższy diagram musimy przepisać na klasy encji, najprościej mówiąc encja to obiekt reprezentujący jeden rekord jednej tabeli, tak więc Encja <strong>Account</strong> będzie odzwierciedleniem strukturę tabeli <strong>account.</strong></p>
<p>Encje jakie utworzyliśmy podczas projektu powinny wyglądać tak:</p>
<p><strong>Administrator</strong><em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec wpisu)</em>:</p>
<pre class="brush: java; title: ; notranslate">

@Table(name = &quot;administrator&quot;)
@Entity
public class Administrator extends Account { }
</pre>
<p><strong>Account</strong><em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec </em><em>wpisu</em><em>):</em></p>
<pre class="brush: java; title: ; notranslate">

@Entity
@Table(name = &quot;account&quot;)
@Inheritance(strategy = InheritanceType.JOINED)
public class Account {

 @Id
 @GeneratedValue(generator = &quot;account_id&quot;, strategy = GenerationType.SEQUENCE)
 @SequenceGenerator(name = &quot;account_id&quot;, sequenceName = &quot;account_id_seq&quot;)
 Integer id;

 public Integer getId() {
 return id;
 }

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

 @Basic
 @NotNull
 @Size(min = 5, max = 20)
 String username;

 public String getUsername() {
 return username;
 }

 public void setUsername(String username) {
 this.username = username;
 }

 @Basic
 @Size(min = 8, max = 20)
 String password;

 public String getPassword() {
 return password;
 }

 public void setPassword(String password) {
 this.password = password;
 }

 @Transient
 public String repeatedPassword;

 public String getRepeatedPassword() {
 return repeatedPassword;
 }

 public void setRepeatedPassword(String repeatedPassword) {
 this.repeatedPassword = repeatedPassword;
 }

 @Basic
 @DateTimeFormat(iso = ISO.DATE_TIME)
 Date addDate = new Date();

 public Date getAddDate() {
 return addDate;
 }

 public void setAddDate(Date addDate) {
 this.addDate = addDate;
 }

 @OneToMany(cascade = CascadeType.ALL, mappedBy = &quot;account&quot;, fetch = FetchType.EAGER, targetEntity = AccountRole.class)
 List&lt;AccountRole&gt; accountRole = new ArrayList&lt;AccountRole&gt;();

 public List&lt;AccountRole&gt; getAccountRole() {
 return accountRole;
 }

 public void setAccountRole(List&lt;AccountRole&gt; accountRole) {
 for (AccountRole ar : accountRole) {
 this.addAccountRole(ar);
 }
 }

 public void addAccountRole(AccountRole accountRole) {
 if (!this.accountRole.contains(accountRole)) {
 accountRole.setAccount(this);
 this.accountRole.add(accountRole);
 }
 }
}
</pre>
<p><strong>AccountRole</strong><em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec </em><em>wpisu</em><em>)</em><strong>:</strong></p>
<pre class="brush: java; title: ; notranslate">

@Entity
@Table(name = &quot;account_role&quot;)
public class AccountRole {

 public AccountRole() {
 this.setRole(&quot;ROLE_ANONYMOUS&quot;);
 }

 public AccountRole(String role) {
 this.setRole(role);
 }

 @Id
 @GeneratedValue(generator = &quot;role_id&quot;, strategy = GenerationType.SEQUENCE)
 @SequenceGenerator(name = &quot;role_id&quot;, sequenceName = &quot;account_role_id_seq&quot;, initialValue = 1)
 Integer roleId;

 public Integer getRoleId() {
 return roleId;
 }

 public void setRoleId(Integer roleId) {
 this.roleId = roleId;
 }

 @Basic
 String role;

 public String getRole() {
 return role;
 }

 public void setRole(String role) {
 this.role = role;
 }

 @ManyToOne(targetEntity = Account.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
 @JoinColumn(name = &quot;accountId&quot;, nullable = true)
 Account account;

 public Account getAccount() {
 return account;
 }

 public void setAccount(Account account) {
 this.account = account;
 }

}
</pre>
<p><strong>Warto zwrócić uwagę na adnotacje:</strong></p>
<ol>
<li>@Entity &#8211; oznacza, że dana klasa jest klasą encyjną</li>
<li>@Table &#8211; mówi jaka tabela w bazie odpowiada naszej encji (jeśli ustawimy, hibernate automatycznie wyeksportuje naszą encje do danej tabeli)</li>
<li>@Inheritance &#8211; mówi hibernate-owi, że ewentualne zależności (poprzez dziedziczenie) powinny być zapisywane w osobnych tabelach (<strong>UWAGA!</strong> &#8211; domyślnie, hibernate łączy wszelkie zależności w jednej tabeli), mechanizm sam zadba o odpowiednie klucze obce.</li>
<li>@Id &#8211; oznacza wybrane pole jako identyfikator</li>
<li>@GeneratedValue &#8211; konfiguruje metodę generowania wartości dla pola, wykorzystywany w identyfikatorach (autoincrement)</li>
<li>@SequenceGenerator &#8211; konfiguruje generator sekwencji dla wybranego pola</li>
<li>@Basic &#8211; zwykłe pole</li>
<li>@Transistent &#8211; mówi aplikacji, że dane pole nie jest odzwierciedlone w bazie danych</li>
<li>@DateTimeFormat &#8211; ustala format daty, dla pola które datę będzie przechowywać</li>
<li>@ManyToOne &#8211; tworzy relacje wiele-do-jednego</li>
<li>@OneToMany &#8211; tworzy relacje jeden-do-wielu</li>
<li>@JoinColumn &#8211; ustawia kolumnę łączącą dwie tabele</li>
</ol>
<p>Ostatnie adnotacje posłużyły do skonfigurowania relacji <strong>Konto-&gt;Poziom dostępu</strong>, dzięki temu przy pobieraniu informacji o koncie, automatycznie dostaniemy listę poziomów dostępu jakie posiada użytkownik.</p>
<p>Drugą ważną rzeczą w powyższych klasach encji są adnotacje służące walidowaniu Dzięki nim nie musimy zaśmiecać kontrolerów dodatkowymi regułami sprawdzającymi, czy też konfigurować walidacji przez XML, reguły walidacji to kolejno:</p>
<ol>
<li>@NotNull &#8211; pole nie może być nullem</li>
<li>@Size &#8211; wielkość pola musi mieścić się w granicach <strong>min</strong> do <strong>max</strong> znaków</li>
</ol>
<p>Oczywiście, to nie jedyne walidatory jakie są dostępne, pełną listę podstawowych walidatorów znajdziecie w <a href="http://java.sun.com/javaee/6/docs/api/javax/validation/constraints/package-summary.html" target="_blank">dokumentacji</a>.</p>
<p>Dodawanie administratorów będzie zapisane w kontrolerze <strong>AdministratorController</strong>, na początek zaimplementujemy metodę która będzie wyświetlać formularz rejestracyjny.</p>
<p><strong>AdministratorController</strong><em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec </em><em>wpisu</em><em>)</em><strong>:</strong></p>
<pre class="brush: java; title: ; notranslate">

@Controller
public class AdministratorController {

 @Autowired
 AccountService accountService;

 @RequestMapping(value = &quot;/administrator/add&quot;, method = RequestMethod.GET)
 public ModelAndView add() {
 ModelAndView mav = new ModelAndView(&quot;/administrator/add&quot;);
 mav.addObject(&quot;administrator&quot;, new Administrator());
 return mav;
 }

}
</pre>
<p>Co warto zauważyć?</p>
<ol>
<li><em>@Controler</em> &#8211; każdy kontroler oznaczony musi być adnotacją @Controler, dzięki czemu spring podczas skanowania pakietu dodaje go do listy</li>
<li><em>@Autowired </em><strong>- </strong>mówi Spring-owi aby poszukał obiektu o zadanym typie i automatycznie podpiął go do pola</li>
<li><em>@RequestMapping</em> &#8211; mówi springowi jakie żądanie ma być skierowane do danej metody, opcja <em>RequestType</em> wskazuje dodatkowo jaki typ żądania ma być tam skierowane,w tym przypadku zależy nam na żądaniu GET</li>
</ol>
<p>Powyższa metoda zwraca obiekt ModelAndView (standardowy obiekt zwracany przez większość metod) który przechowuje informacje na temat pliku widok oraz obiekt klasy Administrator, obiekt ten zbindujemy w widoku dzięki czemu wartości pól w formularzu zostaną przepisane do odpowiadających im wartości pól w obiekcie.<br />
Widok formularza prezentuje się następująco:</p>
<pre class="brush: xml; title: ; notranslate">

&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot; %&gt;
&lt;%@ taglib prefix=&quot;tag&quot; uri=&quot;http://www.springframework.org/tags&quot; %&gt;
&lt;div&gt;
 &lt;form:form commandName=&quot;administrator&quot;&gt;
 &lt;ol&gt;
 &lt;li&gt;
 &lt;form:label path=&quot;username&quot;&gt;&lt;tag:message code=&quot;administrator.username&quot; /&gt;&lt;/form:label&gt;
 &lt;form:input path=&quot;username&quot; /&gt;
 &lt;form:errors path=&quot;username&quot; /&gt;
 &lt;/li&gt;
 &lt;li&gt;
 &lt;form:label path=&quot;password&quot;&gt;&lt;tag:message code=&quot;administrator.password&quot; /&gt;&lt;/form:label&gt;
 &lt;form:password path=&quot;password&quot; /&gt;
 &lt;form:errors path=&quot;password&quot; /&gt;
 &lt;/li&gt;
 &lt;li&gt;
 &lt;form:label path=&quot;repeatedPassword&quot;&gt;&lt;tag:message code=&quot;administrator.repeatedPassword&quot; /&gt;&lt;/form:label&gt;
 &lt;form:password path=&quot;repeatedPassword&quot; /&gt;
 &lt;form:errors path=&quot;repeatedPassword&quot; /&gt;
 &lt;/li&gt;
 &lt;li&gt;
 &lt;input type=&quot;submit&quot; /&gt;
 &lt;/li&gt;
 &lt;/ol&gt;
 &lt;/form:form&gt;
&lt;/div&gt;
</pre>
<p>Gdzie:</p>
<ul>
<li>taglib &#8211; pozwala stosować rozszerzenia w JSP; <strong>form-</strong>generowanie formularzy, obsługa błedów, <strong>tag</strong>-wyświetlanie wiadomości o których wspomniałem wcześniej</li>
<li>commandName &#8211; to nazwa nadana naszemu obiektowi podczas przekazywania go z kontrolera do widoku,</li>
<li>input path &#8211; to pole w naszym obiekcie</li>
<li>errors &#8211; lista błędów dla danego pola (pojawia się po walidacji)</li>
</ul>
<p>Powyższy formularz prezentuje się następująco:</p>
<div id="attachment_802" class="wp-caption aligncenter" style="width: 310px"><a href="http://darekzon.com/wp-content/uploads/2010/05/BookStore.jpg" rel="lightbox[736]"><img class="size-medium wp-image-802" title="Formularz dodawania administratora" src="http://www2.darekzon.com/wp-content/uploads/2010/05/BookStore-300x145.jpg" alt="Formularz dodawania administratora" width="300" height="145" /></a><p class="wp-caption-text">Formularz dodawania administratora</p></div>
<p>Po wciśnięciu wyślij, dane z formularza już pod postacią obiektu klasy <strong>Administrator</strong> przekazywane są do kolejnej metody która sprawdza poprawność danych, i jeśli wszystko jest jak trzeba zapisuje je. Metoda ta wygląda następująco <em>(pełna zawartość pliku w repozytorium &#8211; zobacz koniec posta)</em>:</p>
<pre class="brush: java; title: ; notranslate">

@RequestMapping(value = &quot;/administrator/add&quot;, method = RequestMethod.POST)
 public ModelAndView add(@Valid Administrator admin, BindingResult result) {
 ModelAndView mav = new ModelAndView(&quot;administrator/add&quot;);
 if (!admin.getPassword().equals(admin.getRepeatedPassword())) {
 result.addError(new FieldError(&quot;administrator&quot;, &quot;repeatedPassword&quot;,
 &quot;notEqual&quot;));
 }
 if (result.hasErrors()) {
 mav.addObject(&quot;administrator&quot;, admin);
 return mav;
 }
 List&lt;AccountRole&gt; ar = new ArrayList&lt;AccountRole&gt;();
 ar.add(new AccountRole(&quot;ROLE_ADMIN&quot;));
 try {
 accountService.registerAccount(admin, ar);
 } catch (UserExistsException e) {
 result.reject(&quot;user.exists&quot;);
 mav.addObject(&quot;administrator&quot;, admin);
 return mav;
 }
 mav.setViewName(&quot;redirect:/administrator.html&quot;);
 return mav;
 }
</pre>
<p>Powyższa metoda przyjmuje dwa argumenty, pierwszym jest nasz obiekt <strong>Administrator</strong> który zawiera dane z formularza, oraz obiekt <strong>BindingResult</strong> który przechowuje informacje na temat ewentualnych błędów które wystąpiły podczas sprawdzania poprawności (zgodnie z adnotacjami w klasach encyjnych). Aby Spring wiedział, że dany obiekt wymaga sprawdzenia poprawności danych należy oznaczyć go adnotacją <strong>@Valid, </strong>dzięki temu, spring automatycznie sprawdzi obiekt, a błędy zapisze w obiekcie BindingResult.</p>
<p>Ponieważ nie istnieje adnotacja walidacyjna (przynajmniej w podstawowym zestawie) która sprawdza czy dwa pola są identyczne musiałem zaprogramować dodatkowe sprawdzanie już w kontrolerze. Gdy akcja wykryje, że hasło oraz jego powtórzona wartość nie są identyczne, doda do obiektu <strong>BindingResult</strong> błąd dla pola <strong>repeatedPassword</strong> przez co formularz nie zostanie zapisany w bazie, a zamiast tego zostanie ponownie wyświetlony z zaznaczonym błędem, jak przykład poniżej:</p>
<div id="attachment_803" class="wp-caption aligncenter" style="width: 310px"><a href="http://darekzon.com/wp-content/uploads/2010/05/BookStore-1.jpg" rel="lightbox[736]"><img class="size-medium wp-image-803" title="Formularz z błędnymi polami" src="http://www1.darekzon.com/wp-content/uploads/2010/05/BookStore-1-300x121.jpg" alt="Formularz z błędnymi polami" width="300" height="121" /></a><p class="wp-caption-text">Formularz z błędnymi polami</p></div>
<p>Jeśli natomiast wszystkie nasze pola zostaną wypełnione poprawnie nastąpi próba zapisania formularza <strong>accountService.registerAccount</strong>, a po poprawnym zapisaniu zostaniemy przekierowani (dyrektywa <strong>&#8222;redirect:/administrator&#8221;</strong>) do strony <strong>/administrator.html.</strong> Akcja rejestracji nowego administratora składa się z 2 plików (nie wliczając interfejsów które te pliki implementują.</p>
<p>Pierwszy plik to <strong>AccountServiceImpl</strong> który jest w tym wypadku głównym plikiem<em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec posta)</em>:</p>
<pre class="brush: java; title: ; notranslate">

@Transactional
@Repository
public class AccountServiceImpl implements AccountService {
 @Autowired
 AccountDao accountDao;

 @Autowired
 PasswordEncoder passwordEncoder;

 @Autowired
 SaltSource saltSource;

 @PersistenceContext
 EntityManager entityManager;

 @Override
 public void registerAccount(Account account,List&lt;AccountRole&gt; ar) throws UserExistsException {
 try{
 accountDao.findUsername(account.getUsername());
 throw new UserExistsException();
 }catch(UserNotFoundException une){
 entityManager.persist(account);
 account.setPassword(passwordEncoder.encodePassword(account.getPassword(), saltSource));
 for(AccountRole arole : ar){
 account.addAccountRole(arole);
 }
 entityManager.flush();
 }

 }

}
</pre>
<p>Nasza klasa oznaczona została dwoma adnotacjami, @Transactional, która mówi że, wszystkie metody w niej zaimplementowane objęte są transakcją, oraz @Repository które pozwala nam na pobranie managera encji poprzez adnotację @PersistenceContext.</p>
<p>Aby nasza metoda działała poprawnie musimy dostarczyć jej obiekt <strong>accountDao</strong> który posłuży do sprawdzenia czy dany użytkownik już istnieje, <strong>manager encji</strong> który zapisze nasz obiekt w bacie, a także obiekty <strong>passwordEncoder</strong> oraz <strong>saltSource</strong> służące szyfrowaniu hasła.</p>
<p>Po wywołaniu metody obiekt <strong>accountDao</strong> znajduje użytkownika o podanej nazwie użytkownika i jeśli go nie znajdzie wyrzuca wyjątek <strong>UserNotFoundException</strong> i gdy ten wyjątek wystąpi możemy zapisywać użytkownika do bazy. Nasz administrator zapisywany jest metodą <strong>persist</strong> z obiektu <strong>entityManager</strong>. Dzięki tej metodzie nasza encja zostanie tymczasowo związana z rekordem w bazie danych co oznacza, że wszelkie modyfikacje administratora będą automatyczne odzwierciedlane w bazie danych (więcej o cyklu życia encji w <a href="http://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_em_lifecycle.html" target="_blank">dokumentacji</a>). Gdy nasz użytkownik zostanie zapisany musimy zaktualizować mu hasło, dzieje się tak, gdyż hasło szyfrowane algorytmem sha256 z dodatkową solą w postaci identyfikatora użytkownika, który nadawany jest dopiero po zapisaniu go do bazy danych.</p>
<p>Po aktualizacji hasła przypisujemy naszemu użytkownikowi odpowiednie poziomy dostępu (może mieć ich więcej jak jeden) poprzez dodanie obiektów <strong>AccountRole</strong> do obiektu <strong>Administrator </strong>(dane zostają zapisane dzięki wcześniejszemu skonfigurowaniu powiązań @ManyToOne i @OneToMany).</p>
<p>Ciekawie wygląda również plik <strong>AccountDaoImpl</strong> który wyszukuje użytkowników po zadanym loginie<em> (pełna zawartość pliku w repozytorium &#8211; zobacz koniec posta)</em>:</p>
<pre class="brush: java; title: ; notranslate">

 public class AccountDaoImpl implements AccountDao {

 @Autowired
 HibernateTemplate template;

 @Autowired
 SessionFactory session;

 public Account findUsername(String username) throws  UserNotFoundException {
 DetachedCriteria dc = DetachedCriteria.forClass(Account.class);
 dc.add(Property.forName(&quot;username&quot;).eq(username));
 List users = template.findByCriteria(dc, 0, 1);

 if (users.size() &gt; 0) {
 return (Account) users.get(0);
 }

 throw new UserNotFoundException();
 }

 }
</pre>
<p>W ciele metody <strong>findUsername</strong> zastosowane zostało wyszukiwanie po kryteriach, <strong>CriteriaApi</strong> daje ogromne możliwości w dynamicznym budowaniu zapytań do bazy, w naszym przypadku wskazaliśmy, że dane zapytanie dotyczyć będzie klasy <strong>Account</strong> (a tym samym tabeli odzwierciedlającej tą klasę), oraz dodaliśmy właściwość pola <strong>username</strong> ustalając że pole to powinno być równe wartości podanej w argumencie<em>.</em></p>
<h4>Wyświetlamy użytkowników</h4>
<p>Jeśli nasi administratorzy zostali zapisani do bazy, warto by ich wyświetlić w liście, w tym celu stworzymy metodę obsługującą żądanie <strong>/administrator.html</strong>. Metoda ta znajduje się w pliku <strong>AdministratorController</strong> i wygląda następująco:</p>
<pre class="brush: java; title: ; notranslate">

@RequestMapping(value = &quot;/administrator&quot;)
 public ModelAndView index() {
 ModelAndView mav = new ModelAndView(&quot;/administrator/index&quot;);
 List&lt;String&gt; roles = new ArrayList&lt;String&gt;();
 roles.add(&quot;ROLE_ADMIN&quot;);
 List&lt;Account&gt; acc = accountService.listAccounts(roles);
 mav.addObject(&quot;accounts&quot;, acc);
 return mav;
 }
</pre>
<p>Metoda listująca pobiera z bazy wszystkich użytkowników o zadanym poziomie dostępu (<strong>AccountRole</strong>) po czym przekazujemy listę do widoku. Metoda wyszukująca użytkowników zaimplementowana jest w dwóch plikach, pierwszy <strong>AccountServiceImpl</strong> poniżej:</p>
<pre class="brush: java; title: ; notranslate">

@Override
 @Transactional(readOnly=true)
 public List&lt;Account&gt; listAccounts(List&lt;String&gt; roles) {
 return accountDao.listAccounts(roles);
 }
</pre>
<p>Ma jedynie za zadanie wywołać metodę wyszukującą z obiektu <strong>AccountDaoImpl</strong>, warto wyjaśnić adnotację @Transactional, ponieważ wszystkie metody w klasie <strong>AccountServiceImpl</strong> objęte są transakcją warto przy metodach które nie zapisują danych zaznaczyć, że transakcja ma być traktowana jako tylko do odczytu, dzięki temu zyskujemy na wydajności.</p>
<p>Metoda wywołana przez powyższą metodę ma postać:</p>
<p><span style="text-decoration: line-through;">@Override<br />
public List listAccounts(List roles) {<br />
Criteria criteria = session.openSession().createCriteria(Account.class);<br />
criteria.addOrder(Order.asc(&#8222;username&#8221;));<br />
criteria.createCriteria(&#8222;accountRole&#8221;).add(Restrictions.in(&#8222;role&#8221;,roles));<br />
return criteria.list();<br />
}</span></p>
<p><span style="text-decoration: line-through;">Również tutaj zastosowanie znalazły kryteria, choć w troszkę inny sposób. W tym przypadku wyszukujemy danych dla klasy <strong>Account</strong> sortując je (criteria.addOrder) według nazwy użytkownika, przy czym wszystkie zwracane rekordy powinny posiadać powiązany rekord z tabeli reprezentowanej przez obiekt accountRole który pole <strong>role</strong> ma zadaną wartość (tu <em>ROLE_ADMIN)<strong>. </strong></em>Ponieważ do wyszukania poziomów dostępu musimy złączyć dwie tabele robimy to poprzez stworzenie nowych cryteriów &#8222;na obiekcie&#8221; już aktywnych kryteriów.</span></p>
<p>UPDATE 1</p>
<p>W komentarzach zauważyliście, że w aplikacji mieszam JPA z hibernatem, aby to poprawić stworzyłem nową metodę z wykorzystaniem JPA QL:</p>
<pre class="brush: java; title: ; notranslate">

@Override
 public Collection&lt;Account&gt; listAccounts(Collection&lt;AccountRole&gt; roles){
 List&lt;String&gt; c = new ArrayList&lt;String&gt;(roles.size());
 for(AccountRole in : roles){
 c.add(in.getRole());
 }
 List&lt;Account&gt; accounts = entityManager.createQuery(&quot;SELECT a FROM Account a JOIN a.accountRole r WHERE r.role IN(?1)&quot;,Account.class).setParameter(1,c).getResultList();
 return accounts;
 }
</pre>
<p>Powyższa metoda przyjmuje jako argument kolekcję ról do jakich muszą należeć konta które pobieramy. Kolekcję obiektów musimy przekrztałcić na kolekcję prymitywów (w naszym wypadku ciągów znaków &#8211; STRING).<br />
Następnie przy pomocy managera encji (entityManager) tworzymy zapytanie (createQuery) które przyjmuje tylko jeden parametr przekazywany poprzez setParameter.</p>
<p>Gdy znajdziemy wszystkich użytkowników przekazujemy ich listę do widoku i generujemy:</p>
<pre class="brush: xml; title: ; notranslate">

&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jstl/core_rt&quot; %&gt;

&lt;c:choose&gt;
 &lt;c:when test=&quot;${not empty accounts}&quot;&gt;
 &lt;c:forEach items=&quot;${accounts}&quot; var=&quot;account&quot; varStatus=&quot;status&quot;&gt;
 ${status.index} ${account.username}
 &lt;/c:forEach&gt;
 &lt;/c:when&gt;
 &lt;c:otherwise&gt;
 &lt;tag:message code=&quot;list.no-entry&quot; /&gt;
 &lt;/c:otherwise&gt;
&lt;/c:choose&gt;
</pre>
<h2>Co dalej</h2>
<p>W kolejnej części tutoriala postaramy się zabezpieczyć nasz panel administracyjny poprzez zastosowanie SpringSecurity, ponieważ w tej chwili panel administracyjny wygląda <strong>biednie</strong>, wykorzystamy sitemesh, jsp oraz html by poprawić jego wygląd. A jeśli zostanie trochę miejsca i czasu zajmiemy się&#8230;. (<em>może niespodzianka)</em></p>
<p><em>PS. Zauważyłem, że ta część tutoriala zajęła mi dużo czasu, postanowiłem rozbić go na mniejsze ale częściej występujące wpisy.<br />
</em></p>
<h3>Jak pobrać projekt</h3>
<blockquote><p>Projekt został umieszczony na serwerach <a title="GitHub - social coding" href="http://github.com/" target="_blank">GitHub.com</a>. Adres bezpośredni do projektu: <a title="Bookstore" href="https://github.com/darek/bookstore" target="_blank">https://github.com/darek/bookstore</a>.<br />
Dodatkowo zostało uruchomione repozytorium <strong>GIT</strong> dla wszystkich chętnych i dostępny jest pod adresem: <a title="Repozutorium projektu Bookstore" href="git://github.com/darek/bookstore.git" target="_blank">git://github.com/darek/bookstore.git</a></p></blockquote>
<hr />
<ol>
<li>Gdzie TOMCAT_HOME jest katalogiem głównym tomcata</li>
</ol>
<div class="shr-publisher-736"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2010/05/spring-framework-3-0-tutorial-%e2%80%93-cz-2-%e2%80%93-baza-danych-walidacja-wiadomosci-encje-hibernate/feed</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Spring Framework 3.0 Tutorial &#8211; cz 1 &#8211; przygotowanie projektu, witaj świecie</title>
		<link>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie</link>
		<comments>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie#comments</comments>
		<pubDate>Tue, 13 Apr 2010 20:16:17 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[sitemesh]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=691</guid>
		<description><![CDATA[Zanim przystąpimy do tworzenia projektu warto odpowiednio przygotować sobie zaplecze techniczne. Z racji, że w eclipse pisze znaczna część firm (przynajmniej polskich), a ja jeszcze z niego nie korzystałem w tego typu projektach postanowiłem, że to będzie dobry moment by &#8230; <a href="http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Zanim przystąpimy do tworzenia projektu warto odpowiednio przygotować sobie zaplecze techniczne. Z racji, że w eclipse pisze znaczna część firm (przynajmniej polskich), a ja jeszcze z niego nie korzystałem w tego typu projektach postanowiłem, że to będzie dobry moment by się z nim zmierzyć.<br />
Eclipse wyposażony zostanie dodatkowo we wtyczkę m2Eclipse która pomoże mi w zarządzaniu maven-em. Aplikacja uruchomiona zostanie na kontenerze aplikacji Tomcat, a dane przechowam w bazie PostreSQL. Z racji, że źródła umieszczane będą w zewnętrznym repozytorium opartym o GIT potrzebny jest też ten właśnie program, ja preferuję korzystać z gita poprzez konsolę, oczywiście jeśli ktoś woli można doinstalować odpowiednią wtyczkę bądź nakładkę graficzną.</p>
<p>Co przyda się przy tworzeniu:</p>
<ul>
<li><a href="http://www.eclipse.org/" target="_blank">Eclipse</a></li>
<li><a href="http://maven.apache.org/" target="_blank">maven2</a> i plugin <a href="http://m2eclipse.sonatype.org/" target="_blank">m2Eclipse</a></li>
<li><span style="color: #888888;"><span style="color: #000000;"><a href="http://tomcat.apache.org/tomcat-6.0-doc/index.html" target="_blank">Tomcat 6</a></span></span></li>
<li><a href="http://www.postgresql.org/" target="_blank">PostgreSQL</a></li>
<li><a href="http://git-scm.com/" target="_blank">GIT</a></li>
<li><a href="http://hudson-ci.org/" target="_blank">Hudson</a> <span style="color: #888888;">- bo czemu nie<span id="more-691"></span></span></li>
</ul>
<h2>Podstawowe założenia</h2>
<p>Aplikacja składać się będzie z dwóch modułów, panelu administracyjnego dostępnego tylko i wyłącznie dla użytkowników z poziomem dostępu <strong>ADMIN</strong> oraz części otwartej do której dostęp będą mieli wszyscy internauci (za wyjątkiem obszarów które będą wymagały zalogowania się z poziomem dostępu <strong>CLIENT</strong>). Podział taki można wykonać na dwa sposoby, pierwszym jest stworzenie jednego projektu w którym dokonamy logicznego podziału widoków jak i kontrolerów na panel administracyjny oraz strefę otwartą, spring security dodatkowo zabezpieczy wejście. Wyniki składane będą przez <strong>sitemesh</strong> dzięki któremu można stworzyć oddzielny layout dla panelu administracyjnego oraz dla części otwartej. Jedynym plusem tego rozwiązania jest łatwość operowania plikami w projekcie(o ile nie jest duży), wszystko mamy w jednym miejscu dzięki czemu nie trzeba przechodzić między projektami i szukać plików jeśli tylko chcemy coś zmienić. Niestety przeciw temu rozwiązaniu jest cała reszta, kod staje się mniej czytelny, potrzebne są dodatkowe obejścia które rozdzielać będą żądania do panelu administracyjnego jak i strony klienckiej, nie możliwe jest również umieszczenie osobno części administracyjnej na np. naszym serwerze wewnętrznym. Pomimo tego, że pliki poukładane mamy w jednym projekcie co z początku może wydawać się plusem w większych projektach staje się zmorą, gdyż większa ilość plików powoduje utrudnienia w odszukaniu tego który chcemy zmodyfikować (wiem, przeżyłem to na własnej skórze).</p>
<p>Drugim rozwiązaniem jest stworzenie osobnych projektów dla panelu administracyjnego oraz części klienckiej. Dzięki takiemu rozwiązaniu zachowujemy pewien ład i porządek w strukturze folderów projektów oraz plikach (mniej plików mniej problemu). Dzięki rozdzieleniu plików konfiguracyjnych zyskujemy większą elastyczność oraz możliwość łatwiejszego dostosowania ustawień. Część wspólna obu projektów (klasy związane z obiektami bazy danych) trzymane są w projekcie który dołączany jest jako biblioteka. Mój wybór padł właśnie na tą metodę.</p>
<p>Podsumowując, utworzone zostaną 3 projekty: bookstore, bookstore-admin oraz bookstore-lib.</p>
<h2>Zaczynamy</h2>
<p>Projekt zaczniemy od stworzenia prostej strony a&#8217;la Witaj Świecie!. Aby zacząć całą zabawę otwieramy Eclipse i tworzymy nowy projekt typu &#8222;<strong>Dynamic Web Project</strong>&#8221; (File -&gt; New -&gt; Project&#8230; -&gt; Web -&gt; Dynamic Web Project). Następnie wpisujemy nazwę projektu i klikamy <strong>finish</strong>.<br />
Gdy nasz projekt zostanie utworzony musimy dodać do niego obsługę mavena. Jeśli mamy zainstalowaną wtyczkę m2Eclipse wystarczy kliknąć prawym klawiszem myszy na nasz projekt i wybrać odpowiednio <strong>Maven</strong> -&gt; <strong>Enable Dependency Managment</strong>. Plugin doda do naszego projektu plik <strong>pom.xml</strong>, który zawiera wszelkie informacje potrzebne dla maven-a. W strukturze projektu możemy zauważyć folder <strong>WebContent</strong> który zawierać będzie pliki konfiguracyjne, tłumaczące, pliki jsp oraz wszystko co potrzebne do wyświetlania wyników. Ponieważ nazwa folderu nie przypadła mi do gustu zamieniłem ją na <strong>war </strong>(co będziecie mogli zobaczyć po pobraniu projektu).</p>
<div id="attachment_695" class="wp-caption aligncenter" style="width: 227px"><a href="http://darekzon.com/wp-content/uploads/2010/04/Java-EE-bookstore_pom.xml-Eclipse-_Users_darek_Projects-3.jpg" rel="lightbox[691]"><img class="size-full wp-image-695" title="Struktura projektu z uruchomioną opcją Dependency Managment" src="http://www3.darekzon.com/wp-content/uploads/2010/04/Java-EE-bookstore_pom.xml-Eclipse-_Users_darek_Projects-3.jpg" alt="" width="217" height="150" /></a><p class="wp-caption-text">Struktura projektu z uruchomioną opcją Dependency Managment</p></div>
<h2>Dodawanie zależności</h2>
<p>Pierwszą rzeczą jaką robię w nowo utworzonym projekcie jest dodanie podstawowych zależności. Z mojej strony są to takie biblioteki jak sitemesh 3 (UWAGA! &#8211; wydanie alpha, mam nadzieję, że działa), spring-web, spring-context, czy też spring-webmvc; czyli wszystko co potrzebne na dobry początek.<br />
Na koniec dodaję biblioteki JUnit oraz Mockito które posłużą do testowania aplikacji. Ponieważ biblioteki te nie będą nam potrzebne w działającej aplikacji, warto zaznaczyć w ich zakresie (ang. scope) opcje &#8222;<strong>test</strong>&#8221; dzięki czemu będą dołączane tylko podczas testów.</p>
<div id="attachment_696" class="wp-caption aligncenter" style="width: 310px"><a href="http://darekzon.com/wp-content/uploads/2010/04/Java-EE-bookstore_pom.xml-Eclipse-_Users_darek_Projects.jpg" rel="lightbox[691]"><img class="size-medium wp-image-696" title="<Java EE> &#8211; bookstore_pom.xml &#8211; Eclipse &#8211; _Users_darek_Projects&#8221; src=&#8221;http://www1.darekzon.com/wp-content/uploads/2010/04/Java-EE-bookstore_pom.xml-Eclipse-_Users_darek_Projects-300&#215;213.jpg&#8221; alt=&#8221;Zarządzanie zależnościami w Eclipse&#8221; width=&#8221;300&#8243; height=&#8221;213&#8243; /></a><p class="wp-caption-text">Zarządzanie zależnościami w Eclipse</p></div>
<h2>Konfiguracja</h2>
<p>Podstawowa konfiguracja obejmuje dwa pliki, najważniejszy <strong>web.xml</strong> oraz podstawowa konfiguracja spring-a i głównych servletów w pliku <strong>web-servlet.xml<br />
</strong>Plik <strong>web.xml</strong> jest jak najbardziej zwyczajny, podana jest lokalizacja pliku <strong>web-servlet.xml</strong>, skonfigurowane zostały listenery, a także servlety wraz z mapowaniem.</p>
<p><strong>web.xml:</strong></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;

&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;

xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; xmlns:web=&quot;http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot;

xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot;

id=&quot;b0ok&quot; version=&quot;2.5&quot;&gt;

&lt;display-name&gt;bookstore&lt;/display-name&gt;

&lt;!--

wskazujemy lokalizacje plikow konfiguracyjnych

--&gt;

&lt;context-param&gt;

&lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;

&lt;param-value&gt;

/WEB-INF/web-servlet.xml

&lt;/param-value&gt;

&lt;/context-param&gt;

&lt;listener&gt;

&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;

&lt;/listener&gt;

&lt;!--

tworzymy servlet ktory obslugiwac bedzie nasze zapytania

--&gt;

&lt;servlet&gt;

&lt;servlet-name&gt;web&lt;/servlet-name&gt;

&lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;

&lt;/servlet&gt;

&lt;!--

ustalamy ze nasz servlet obslugiwac bedzie tylko zapytania konczace

sie rozszerzeniem *.html

--&gt;

&lt;servlet-mapping&gt;

&lt;servlet-name&gt;web&lt;/servlet-name&gt;

&lt;url-pattern&gt;*.html&lt;/url-pattern&gt;

&lt;/servlet-mapping&gt;

&lt;!--

ustalamy, jakie pliki beda ladowane gdy aplikacja dostanie zapyanie

&quot;/&quot; (np. http://example.com/)

--&gt;

&lt;welcome-file-list&gt;

&lt;welcome-file&gt;redirect.jsp&lt;/welcome-file&gt;

&lt;/welcome-file-list&gt;

&lt;/web-app&gt;
</pre>
<p>Dodatkowo wskazaliśmy plik który jest ładowany, gdy serwer otrzyma żądanie <strong>&#8222;</strong>/<strong>&#8221; </strong>(czyli strona startowa), w tym przypadku jest to załadowanie pliku redirect.jsp który przekieruje nas na stronę główną:</p>
<p><strong>redirect.jsp:</strong></p>
<pre class="brush: xml; title: ; notranslate">
&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot;%&gt;

&lt;jsp:forward page=&quot;/index.html&quot; /&gt;
</pre>
<p><strong><span style="color: #808080;">Update 1:</span></strong><span style="color: #808080;"><br />
Jak trafnie zauważył </span><a href="http://chlebik.wordpress.com/" target="_blank"><span style="color: #808080;">Chlebik</span></a><span style="color: #808080;"> (zob. </span><a href="http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie#comment-319"><span style="color: #808080;">komentarz</span></a><span style="color: #808080;">), zamiast redirect.jsp możemy śmiało napisać &#8222;index.html&#8221; wtedy spring wykona akcję przeznaczoną dla żądania &#8222;/&#8221;, przy czym, należy dodać plik index.html do głównego katalogu (war/webContent) aplikacji, może być nawet pusty, ważne by spring wiedział, że jest. Za podpowiedź dziękuję. </span></p>
<p>Plik web-servlet.xml na początek wskażemy gdzie aplikacja ma szukać kontrolerów, oraz zaznaczymy, że będzie ona działała w oparciu o <a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html" target="_blank">adnotacje</a>.<br />
<strong>web-servlet.xml</strong></p>
<pre class="brush: xml; title: ; notranslate">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot; xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd&quot;&gt;
&lt;context:component-scan base-package=&quot;com.darekzon.bookstore.controller&quot; /&gt;
&lt;context:annotation-config /&gt;
&lt;context:spring-configured /&gt;
&lt;bean
class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
&lt;property name=&quot;prefix&quot; value=&quot;/views/&quot; /&gt;
&lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;
&lt;/bean&gt;
&lt;/beans&gt;</pre>
<p>W sekcji &lt;<strong>context:component-scan</strong>&gt; wskazujemy, który pakiet ma być skanowany w poszukiwaniu kontrolerów (klas z adnotacją <em>@Controller</em>), jeśli chcemy możemy wskazać więcej jak jeden pakiet przy czym trzeba pamiętać, że nazwy klas muszę się różnić, więc jeśli mamy <strong>IndexController</strong> w naszym pakiecie to w pakiecie <em>com.darekzon.bookstore.controller</em> musimy umieścić <strong>Index2Controller</strong>.</p>
<p>Ach, no i ostatni blok wskazuje gdzie spring ma szukać plików jsp za pomocą których generować będzie wyniki.</p>
<h2>Pierwszy kontroler</h2>
<p>Aplikacja skonfigurowana, pierwszy kontroler będzie wyświetlał legendarny napis &#8222;Witaj świecie!&#8221;. Sam kontroler jest wyjątkowo prosty, posiada tylko jedną metodę, która zwraca obiekt typu &#8222;<strong>ModelAndView</strong>&#8221; przechowujący nazwę widoku do wyrenderowania. Kontroler zapisany jest w pliku IndexController.java (konwencja nazewnicza nie jest obowiązkowa) a jego zawartość przedstawia się jak poniżej:</p>
<pre class="brush: java; title: ; notranslate">package com.darekzon.bookstore.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

@RequestMapping(value = &quot;/index&quot;)
public ModelAndView index(){
ModelAndView mav = new ModelAndView(&quot;index/index&quot;);

return mav;
}

}</pre>
<p>Warto tutaj zwrócić uwagę na 3 rzeczy:</p>
<ol>
<li>Każdy kontroler powinien mieć adnotację <strong>@Controller</strong>, dzięki temu spring podczas skanowania pakietów (zob. konfigurację web-servlet.xml ↑)</li>
<li>Spring szuka metody którą ma wykonać na podstawie adnotacji <strong>@RequestMapping</strong>, w naszym przypadku czeka aż wywołamy żądanie &#8222;<strong>/index</strong>&#8222;,  warto dodać, że nie można dodać dwóch metod z tą samą wartością tej adnotacji (podczas uruchamiania wyskoczy wyjątek)</li>
<li>W tym przypadku zwracamy obiekt <strong>ModelAndView</strong> który zawiera nazwę pliku widoków który posłuży do renderowania wyników, plik widoku powinien znajdować się w folderze <strong>/views/index/index.jsp</strong>, czyli według ustawień w pliku <strong>web-servlet.xml</strong> oraz wartości zwracanej przez metodę.</li>
</ol>
<p>Kontroler gotowy, pora na widok który będzie renderowany, ponieważ będzie to wyświetlenie tekstu &#8222;<strong>Witaj świecie!</strong>&#8221; plik ten nie jest skomplikowany (kolejne na pewno wniosą coś nowego w tej kwestii), a jego zawartość przedstawia się następująco:</p>
<pre class="brush: xml; title: ; notranslate">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Index&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
Witaj świecie!
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Tu tłumaczyć raczej nic nie trzeba, no może poza faktem pisania kodu całej strony w wyniku, ale o tym za chwilę.</p>
<h2>Sitemesh i konfiguracja szablonów</h2>
<p><a href="http://www.sitemesh.org/" target="_blank">Sitemesh</a> to ciekawy projekt dzięki któremu znacznie ułatwimy sobie zabawę z wyglądem strony, jego głównym zadaniem jest sklejanie widoków wygenerowanych przez &#8222;aplikację&#8221; z ogólnym szablonem, oraz doklejanie części wspólnych między widokami (np. mamy oddzielny layout dla strony głównej, kontaktu oraz reszty serwisu, przy czym menu jest takie samo).</p>
<div id="attachment_701" class="wp-caption aligncenter" style="width: 222px">&#8222;]<a href="http://darekzon.com/wp-content/uploads/2010/04/overview.gif" rel="lightbox[691]"><img class="size-medium wp-image-701" title="Sitemesh - sposób działania" src="http://www1.darekzon.com/wp-content/uploads/2010/04/overview-212x300.gif" alt="Sitemesh - sposób działania" width="212" height="300" /></a><p class="wp-caption-text">Sposób działania Sitemesh-a[1</p></div>
<p style="text-align: center;">W projekcie użyję najnowszej (3.0alpha-1) wersji sitemesh-a, ponieważ nie jest to jeszcze stabilna wersja (nawet nie beta), zalecam do poważnych projektów używać wersję 2.4 dostępną pod starą <a href="http://www.opensymphony.com/sitemesh/" target="_blank">stroną projektu</a>.</p>
<p>Konfiguracja sitemesh-a może odbywać się na dwa sposoby: klasa Java rozszerzająca klasę<strong> ConfigurableSiteMeshFilter</strong> lub plik XML, ponieważ z niewiadomych przyczyn konfiguracja przez XML mi nie działa skupię się na konfigurowaniu z wykorzystaniem JAVY.</p>
<p>W pliku web.xml musimy dodać filtry które będą go uruchamiać dla każdego żądania (przynajmniej w moim przypadku), wskazując przy tym naszą klasę konfigurującą, kod wygląda następująco:</p>
<pre class="brush: xml; title: ; notranslate">&lt;filter&gt;
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;
&lt;filter-class&gt;com.darekzon.bookstore.filter.Sitemesh&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;sitemesh&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</pre>
<p>Zaś klasa <strong>Sitemesh </strong>konfigurująca ma postać:</p>
<pre class="brush: java; title: ; notranslate">public class Sitemeshextends ConfigurableSiteMeshFilter {
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
builder.addDecoratorPath(&quot;/*&quot;, &quot;/views/layout/default.jsp&quot;);
}
}</pre>
<p>Działanie proste, przechwytujemy wyniki żądań wysłanych do aplikacji, i &#8222;kompilujemy&#8221; je z dekoratorem z pliku &#8222;<strong>/views/layout/default.jsp</strong>&#8221; (UWAGA! powinno podawać się pliki z rozszerzeniami innymi od tych zdefiniowanych w filtrach w pliku web.xml &#8211; w moim wypadku .html).</p>
<p>W tej chwili dekorator wygląda następująco:</p>
<pre class="brush: xml; title: ; notranslate">&lt;!doctype html&gt;
&lt;html lang=&quot;pl&quot; dir=&quot;ltr&quot;&gt;
&lt;head&gt;
&lt;title&gt;&lt;sitemesh:write property='title' /&gt; - BookStore&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/resource/style/default.css&quot; /&gt;
&lt;sitemesh:write property='head' /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;sitemesh:write property='body' /&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Podczas renderowania sitemesh otwiera dekorator oraz plik wynikowy kontrolera oraz je scala (nie chodzi o język programowania), w miejscu tagu <strong>&lt;sitemesh:write property=&#8217;title&#8217; /&gt;</strong> wstawia wartość tagu &lt;title&gt; wyrenderowanego wyniku, w miejsce tagu<strong> &lt;sitemesh:write property=&#8217;head&#8217; /&gt;</strong> wartość tagu head (za wyjątkiem title), a tam gdzie pyta o <strong>&lt;sitemesh:write property=&#8217;body&#8217; /&gt;</strong>, wszystko co znajduje się w body. Proste, a co ważne wygodne.</p>
<h2>CDN&#8230;</h2>
<p>W następnej części zajmiemy się panelem administracyjnym oraz jego zabezpieczeniem, stworzymy również cały layout tego panelu wykorzystując bardziej zaawansowane techniki dostarczane przez JSP i Sitemesh.Tymczasem zapraszam do pozostawiania komentarzy, wszelkie postaram się wziąć pod uwagę.</p>
<h3>Jak pobrać projekt</h3>
<blockquote><p>Projekt został umieszczony na serwerach <a title="GitHub - social coding" href="http://github.com/" target="_blank">GitHub.com</a>. Adres bezpośredni do projektu: <a title="Bookstore" href="https://github.com/darek/bookstore" target="_blank">https://github.com/darek/bookstore</a>.<br />
Dodatkowo zostało uruchomione repozytorium <strong>GIT</strong> dla wszystkich chętnych i dostępny jest pod adresem: <a title="Repozutorium projektu Bookstore" href="git://github.com/darek/bookstore.git" target="_blank">git://github.com/darek/bookstore.git</a></p></blockquote>
<hr />
<p><sup>1.</sup> Diagram pobrany ze strony <a href="http://www.sitemesh.org/overview.html">http://www.sitemesh.org/overview.html</a></p>
<div class="shr-publisher-691"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-cz-1-przygotowanie-projektu-witaj-swiecie/feed</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>Spring Framework 3.0 Tutorial &#8211; wstęp</title>
		<link>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-wstep</link>
		<comments>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-wstep#comments</comments>
		<pubDate>Fri, 09 Apr 2010 20:17:44 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[Spring framework]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[jasypt]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[sitemesh]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://darekzon.com/?p=674</guid>
		<description><![CDATA[Największym minusem podczas nauki Spring Framework był brak kompleksowych przykładów pokazujących jak zbudować pełną aplikację wykorzystując nie tylko Spring-a, ale również integrując z nim inne rozwiązania. Ten tutorial ma za zadanie uzupełnić tą lukę, choć jego zadanie jest troszkę większe. &#8230; <a href="http://darekzon.com/2010/04/spring-framework-3-0-tutorial-wstep">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Największym minusem podczas nauki Spring Framework był brak kompleksowych przykładów pokazujących jak zbudować pełną aplikację wykorzystując nie tylko Spring-a, ale również integrując z nim inne rozwiązania.</p>
<p>Ten tutorial ma za zadanie uzupełnić tą lukę, choć jego zadanie jest troszkę większe. Pisząc tutorial mam zamiar nie tylko pokazać jak wygląda taka aplikacja, ale również skonsolidować swoją wiedzę oraz nauczyć się czegoś więcej od was (choćby poprzez komentarze). Myślę, że będzie to dobre miejsce na wszelkie dyskusje na temat technologii użytych w projekcie oraz sposobu ich użycia. Z chęcią przyjmę również wszelką KONSTRUKTYWNĄ krytykę.</p>
<p>Projekt jaki tutaj zrealizuje to księgarnia internetowa (helion może zacząć się bać ;) ) która na początku oferować będzie standardowe funkcjonalności (kategorie, książki, wyszukiwarkę, mechanizm zamawiania). Szerzej opiszę wszystko w następnych częściach tutorialu.</p>
<p>W projekcie mam zamiar wykorzystać:</p>
<ul>
<li>Spring Framework 3.0</li>
<li>Hibernate + Hibernate Search (implementacja wyszukiwarki Lucene)</li>
<li>Jasypt</li>
<li>Sitemesh</li>
<li>inne o których jeszcze nie wiem</li>
<li>Lucene (indeksowanie/wyszukiwanie zawartości)</li>
<li>inne</li>
</ul>
<p>Wkrótce podam również adres do repozytorium projektu który umieszczony będzie na serwerach kenai.com (wkrótce w strukturach java.net)</p>
<div class="shr-publisher-674"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2010/04/spring-framework-3-0-tutorial-wstep/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Projekty javove &#8211; Wybór narzędzi</title>
		<link>http://darekzon.com/2009/04/projekty-javove-wybor-narzedzi</link>
		<comments>http://darekzon.com/2009/04/projekty-javove-wybor-narzedzi#comments</comments>
		<pubDate>Mon, 06 Apr 2009 16:23:34 +0000</pubDate>
		<dc:creator>darek</dc:creator>
				<category><![CDATA[Projektowanie aplikacji]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[pom]]></category>
		<category><![CDATA[soap]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[technologie]]></category>
		<category><![CDATA[uml]]></category>

		<guid isPermaLink="false">http://blog.darekzon.com/?p=156</guid>
		<description><![CDATA[Choć stworzenie dużego javovego projektu jest możliwe w zwykłym notatniku (pomijając kompilację oraz serwer obsługujący) jest to raczej mało wygodne a na pewno nie wydajne. Do tej pory zazwyczaj używałem NetBeans-a jako, że głównie piszę aplikacje desktopowe. Jednak do tego &#8230; <a href="http://darekzon.com/2009/04/projekty-javove-wybor-narzedzi">kontynuuj czytanie <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Choć stworzenie dużego javovego projektu jest możliwe w zwykłym notatniku (pomijając kompilację oraz serwer obsługujący) jest to raczej mało wygodne a na pewno nie wydajne. Do tej pory zazwyczaj używałem NetBeans-a jako, że głównie piszę aplikacje desktopowe. Jednak do tego zadania potrzebuję czegoś więcej tak więc mój wybór padł na:<span id="more-156"></span><a title="InteliJ Idea - jetbrains" href="http://www.jetbrains.com/idea/" target="_blank">InteliJ Idea</a> będzie IDE które wesprze mnie w tworzeniu aplikacji JEE. Dlaczego ten wybór? Może na początku dlaczego nie. Wcześniej korzystałem z <a title="Eclipse Project" href="http://www.eclipse.org/" target="_blank">Eclipse</a> jednak jego sposób konfiguracji lekko mnie przeraził, trzeba się naprawdę porządnie na klikać, żeby coś tam dodać (może teraz jest lepiej), jeśli chodzi o <a title="NetBeans project" href="http://www.netbeans.org/" target="_blank">NetBeans</a> w dziedzinie EE dopiero rozpoczyna swoją przygodę (wreszcie naprawdę porządnie skupili się na tej funkcjonalności), niedawna aktualizacja do wersji 6 oraz 6.5(i zapewne przyszła 7) przyniosła naprawdę wiele udogodnień jeśli chodzi o pisanie aplikacji enterprise-owych jednak na ten moment nadal zawiera błędy (albo ja się nie znam &#8211; co w sumie jest prawdą).</p>
<p>Samo InteliJ Idea jest całkiem miłym środowiskiem, wspiera wszystkie technologie które będę używał w moim projekcie (<a title="Spring framework" href="http://www.springsource.org/">Spring</a>, <a title="Hibernate Project" href="http://www.hibernate.org/">Hibernate</a>, <a title="SOAP - wikipedia" href="http://pl.wikipedia.org/wiki/SOAP">SOAP</a>), ma wsparcie dla maven-a a także systemu kontroli wersji <a title="GIT - system kontroli wersji" href="http://git-scm.com/" target="_blank">GIT</a>. Ma bardzo dobry mechanizm podpowiadania składni (szczególnie xml-i), co dodatkowo ułatwia programowanie.</p>
<p>W części desktopowej skorzystam z NetBeans platform, jest to głównie wina <a title="Marek Kliś - blog" href="http://marekklis.blogspot.com/" target="_blank">Marka Klisia</a> który na NetBeans Day w Krakowie pokazywał możliwości tej platformy. Dodam, że przy okazji ostatnich zaliczeń semestralnych stworzyłem niewielką aplikację w tym swing-owym framework-u i muszę przyznać, że naprawde przyśpiesza wszelkie prace (choćby zarządzanie oknami, czy też mechanizm aktualizacji).</p>
<p>Z racji, że moja praca ma zawierać metody projektowania trzeba będzie stworzyć trochę <a title="UML - wikipedia" href="http://pl.wikipedia.org/wiki/UML" target="_blank">UML</a>-i, do tego zadania wykorzystam aplikację <a title="Visual Paradigm for UML" href="http://www.visual-paradigm.com/product/vpuml/" target="_blank">Visual Paradigm for UML</a>. Wybór był raczej jasny, środowisko to dostarcza pełną obsługę chyba wszystkich dostępnych typów UML-i, do tego jest wygodne w użyciu.</p>
<p>Żeby ogarnąć to wszystko wykorzystam projekt <a title="Maven - apache.org" href="http://maven.apache.org/" target="_blank">Maven</a>. Maven jest zarządcą projektów opartym o metodologię <a title="POM - wikipedia" href="http://en.wikipedia.org/wiki/Project_Object_Model" target="_blank">POM</a>. Pomoże mi w zarządzaniu bibliotekami wykorzystywanymi w mojej aplikacji (kolejny plik xml dochodzi do rodziny).</p>
<p>Wcześniej wspomniany GIT zabezpieczy mnie przed wszelkimi błędami programistycznymi(usunę to, chyba jest nie przydatne ;) ) a także błędami ze strony systemu operacyjnego (znikanie plików). GIT został przezemnie niedawno odkryty, wcześniej korzystałem z SVN-a oraz Mercurical. GIT działa naprawde dobrze i jest prosty w użyciu.</p>
<div class="shr-publisher-156"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://darekzon.com/2009/04/projekty-javove-wybor-narzedzi/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

