Michał Mech – W stronę Java

Zapiski programisty

Automatyczne wstrzykiwanie bean’ów w Springu

brak komentarzy

Jednym z najważniejszych modułów frameworka Spring jest jego kontener odwrócenia sterowania (ang. Inversion of Control, IoC), który dostarcza nam możliwości wstrzykiwania zależności (ang. Dependency Injection, DI). Jest to miejsce gdzie dzieje się magia :-)

Taki kontener należy odpowiednio skonfigurować, aby bean‘y zarządzane przez framework były odpowiednio zainicjowane oraz ze sobą połączone. Nie będę opisywał wszystkich aspektów konfiguracji kontenera IoC, gdyż to zadanie na niekrótką książkę. Skupię się tylko na definiowaniu powiązań między bean‘ami za pomocą automatycznego wstrzykiwania (ang. autowire).

Przeczytaj resztę wpisu »

Opublikowany przez Michał Mech

2009-06-16 o 23:13:25

Bye bye Blogger

ilość komentarzy: 2

Drogi czytelniku jeśli jesteś tutaj n-ty raz zauważyłeś zapewne, że zmieniła się szata graficzna.

Postanowiłem podziękować za współpracę platformie Blogger‘a na rzecz własnej instalacji Wordpress‘a. Początkowo był to Blogger ponieważ chciałem po prostu pisać i nie przejmować się aplikacja, instalacją, e.t.c. Z czasem okazało się, że Blogger to zbyt uboga platforma i nie spełnia moich oczekiwań. Na przykład kuleje możliwości wklejania listingów, których jest sporo w moich wpisach.
Wordpress daje nieporównywalnie dużo więcej możliwości, chociażby z tego powodu, że instalacja stoi na “moim” serwerze i mogą z nią robić co chcę.

Jak widać udało mi się bez większego bólu zaimportować treści, które były opublikowane jeszcze za Blogger‘a.

Starałem się zachować stan bloga tak aby migracja była niezauważalna dla osób, które subskrybują kanał RSS lub mają mój adres zapisany w zakładkach. Nie mniej jednak kilka linków zmieniło adresy. Za utrudnienia najmocniej przepraszam. Proszę informować mnie o problemach, postaram się je naprawić. Takie są koszty lenistwa i pójścia na łatwiznę, czym był wybór Blogger‘a.

P.S.
Po przenosinach oraz po wielu innych wydarzeniach, które się ostatnio działy w moim życiu mogę znów zabrać się do nauki, a co z tym się wiąże, do publikowania kolejnych wpisów.

Opublikowany przez Michał Mech

2009-06-09 o 21:58:41

Kategorie Blogowanie

Tagi ,

Budowanie aplikacji za pomocą Mavena

ilość komentarzy: 4

Kiedy pisałem o instalacji Seama wspomniałem, o instalacji narzędzia Maven, które może mi się przydać w przyszłości. I oto nadeszła ta chwila. Czytając jakiś czas temu poradnik Developing a Spring Framework MVC application pomyślałem, że fajnie byłoby zamienić narzędzie do budowania projektu z Anta na Mavena, który zajmie się przy okazji zależnościami.

Korzystam z Eclipse‘a jako IDE więc do zabawy przydadzą mi się dwa pluginy:

  1. Spring IDE – Przyda się do utworzenia Hello World oraz na przyszłość;
  2. Maven Integration for Eclipse – Zautomatyzuje nam to trochę czynności oraz dostaniemy bardzo ładny edytor plików POM.

New Spring Project (3)Po zainstalowaniu powyższych tworzymy zalążek projektu w Eclipse. Wybieramy File > New > Other a później Spring Project. Podajemy nazwę projektu, niech to jExample oraz zmieniamy lokalizację Source Folder Name z src na src/main/java a Output Folder Name z bin na target/classes. Zmiany te wynikają z domyślnych ustawień pluginów Mavena, z których będziemy korzystać.

Create new POMW następnej kolejności aktywujemy zarządzanie projektem za pomocą Mavena. W tym celu wybieramy Maven Enable Dependency Managment po uprzednim kliknięciu prawym klawiszem myszy na projekcie.

Kluczowym elementem pracy z Mavenem jest plik POM, który stanowi konfigurację zawierającą informacje o projekcie, jego zależnościach, e.t.c. Kreator tworzenia pliku POM uruchomi się zaraz po aktywacji Mavena. Jedyną zmianą jakiej dokonujemy to zamiana pakowania projektu z jar na war. Oto plik, który będzie wynikiem:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>jExample</groupId>
    <artifactId>jExample</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
</project>

Utwórzmy teraz naprawdę bardzo prostą aplikację internetową. Niech jej jedyną funkcją będzie przywitanie nas słowami … Hello World. W pierwszej kolejności tworzymy deskryptory.

src/main/webapp/WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>jExample</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jExample</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

src/main/webapp/WEB-INF/jExample-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean name="/index.html" class="pl.michalmech.jexample.web.HelloController"/>
</beans>

Oraz src/main/webapp/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans

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

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

http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

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

        http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
</beans>

Dodajemy jeszcze szybko widok (src/main/webapp/index.jsp):

<html>
    <head>
        <title>jExample</title>
    </head>
    <body>
        <h1>jExample :: Hello World</h1>
    </body>
</html>

Oraz na koniec niewielki kontroler:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloController implements Controller {

    protected final Log logger = LogFactory.getLog(getClass());

    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        logger.info("Returninag hello view");

        return new ModelAndView("index.jsp");
    }
}

Niestety jedyne co ma dla nas Eclipse to błędy wynikające z braku klas, których potrzebujemy:

HelloController

Jest to okazja do wykorzystania wreszcie Mavena, który dostarczy nam zależności, których potrzebujemy. Chcielibyśmy przede wszystkim skorzystać ze Spring MVC (niech to będzie najnowsza wersja) oraz API związanego z sewletami. Dodajemy do pliku POM następujący wpis (możemy oczywiście w tym celu użyć edytora plików POM):

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>2.5.6</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Spring Project (dependencies)POM Editor (dependencies_Zapisanie pliku sprawi, że Maven pobierze ze swojego lokalnego repozytorium wymagane zależności i dostarczy do projektu. W przypadku braku wymaganych paczek, zostaną one wcześniej pobrane z sieci (http://repo2.maven.org/maven2) oraz zainstalowane w repozytorium lokalnym Mavena. Warto zaznaczyć, że Maven dostarczył nam również inne wymagane pośrednio zależności: commons-logging, spring-core, e.t.c. My nie musimy się niczym martwić. Projekt już nie świeci się na czerwono od błędów.

W związku z tym, że dążymy do ujrzenia naszej aplikacji działającej przydałoby sie teraz utworzyć plik WAR. W tym celu wykonujemy w konsoli (będąc w loklizacji, w której znajduje się nasz projekt) polecenie:

mvn clean package

Wynikiem wykonania tego polecenia powinno być coś takiego:

F:\workspace\jExample>mvn clean package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Unnamed - jExample:jExample:war:0.0.1-SNAPSHOT
[INFO]    task-segment: [clean, package]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting directory F:\workspace\jExample\target
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to F:\workspace\jExample\target\classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] No sources to compile
[INFO] [surefire:test]
[INFO] No tests to run.
[INFO] [war:war]
[INFO] Packaging webapp
[INFO] Assembling webapp[jExample] in [F:\workspace\jExample\target\jExample-0.0.1-SNAPSHOT]
[INFO] Processing war project
[INFO] Webapp assembled in[171 msecs]
[INFO] Building war: F:\workspace\jExample\target\jExample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5 seconds
[INFO] Finished at: Wed Apr 08 23:51:16 CEST 2009
[INFO] Final Memory: 11M/20M
[INFO] ------------------------------------------------------------------------

Teraz krótkie wyjaśnienie. Maven w pierwszej kolejności wyczyścił wszystko to co mogło być do tej pory skompilowane i umieszczone w folderze target. Po to było polecenie clean. W dalszej części Maven zajmuje się pakowaniem projektu (polecenie package). Plan tej czynności zakłada wykonanie kilku czynności wcześniej. Jest to przede wszystkim kompilacja źródeł, zebranie zasobów, wykonanie testów jednostkowych oraz wreszcie zebranie wszystkiego do kupy w WAR Pomimo tego, że w tym przypadku niewiele faktycznie jest do zrobienia, Maven informuje nas o każdej czynności i wynikach jej wykonania.
Każda z czynności jest realizowana przez oddzielny plugin Mavena, który możemy w szczególności skonfigurować wedle naszych życzeń.

Do Hello World krzyczącego do nas z okna przeglądarki zostało bardzo niewiele. Podobnie jak w tutorialu, o którym wspomniałem na początku, my również skorzystamy z kontenera aplikacji Apache Tomcat (instalacja jest niezwykle prosta więc pominę jej opis).

Przy tej okazji chciałbym zwrócić uwagę na konfigurację zależności servlet-api. Przestrzeń (ang. scope) jest zdefiniowana jako provided. Dzieje się tak ponieważ Tomcat dostarcza już tą paczkę wśród swoich bibliotek więc aplikacja, która jest rozmieszczana (ang. deploy) na serwerze nie musi jej zawierać.

W folderze domowym instalacji Mavena, w folderze conf znajduje się plik z globalną konfiguracją: settings.xml. W bloku zawierającym definicje serwerów servers/servers dodajmy opis lokalnego Tomcata:

<server>
    <id>tomcat.local</id>
    <username>admin</username>
    <password></password>
</server>

Identyfikator serwera jest dowolny natomiast login oraz hasło (a dokładniej jego brak) wynika z domyślnych wartości dla standardowej instalacji Tomcataa. Aby umieścić naszą aplikację na serwer skorzystamy z pluginu Tomcat Maven Plugin. Aby wykonać całą robotę, czyli kompilację, spakowanie oraz rozmieszczenie (ang. deploy) projektu na serwer wystarczy wykonać polecenie:

mvn clean package tomcat:deploy

W zasadzie to wystarczy ponieważ Maven pobierze ze swojego repozytorium i zainstaluje wspomniany plugin jak tylko będzie go potrzebował. Podczas wykonywania polecenia zobaczymy to w konsoli:

[INFO] Searching repository for plugin with prefix: 'tomcat'.
[INFO] org.codehaus.mojo: checking for updates from central
[INFO] artifact org.codehaus.mojo:tomcat-maven-plugin: checking for updates from central
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/tomcat-maven-plugin/1.0-beta-1/tomcat-maven-plugin-1.0-beta-1.pom
5K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/mojo-parent/20/mojo-parent-20.pom
18K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/tomcat-maven-plugin/1.0-beta-1/tomcat-maven-plugin-1.0-beta-1.jar
48K downloaded

Niemniej jednak gdybyśmy chcieli wskazać jakieś inne repozytorium, które Maven ma przeszukiwać w chwili kiedy czegoś potrzebuje, możemy je zdefiniować w pliku POM:

<repositories>
    <repository>
        <id>mojo</id>
        <name>Mojo Project</name>
        <url>http://repository.codehaus.org</url>
    </repository>
</repositories>

Podobnie ma się rzecz z uruchomieniem plugin. Użyta będzie domyślna konfiguracja. Aby ją zmienić powinniśmy odpowiednio zmodyfikować POM. Oto przykład jak jawnie podać nazwę serwera, na którym umieszczamy (ang. deploy) aplikację:

<build>
    <plugins>
        <plugin>
            <groupid>org.codehaus.mojo</groupid>
            <artifactid>tomcat-maven-plugin</artifactid>
            <configuration>
                <server>tomcat.local</server>
            </configuration>
        </plugin>
    </plugins>
</build>

Konfiguracja oczywiście będzie będzie wzięta z ustawień globalnych Mavena.

W zasadzie to wszystko. Wykonanie wspomnianego polecenia (mvn clean package tomcat:deploy) powinno dać następujący wynik:

...
[INFO] Building war: F:\workspace\jExample\target\jExample-0.0.1-SNAPSHOT.war
[INFO] [tomcat:deploy]
[INFO] Deploying war to http://localhost:8080/jExample
[INFO] OK - Deployed application at context path /jExample
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------

Uruchomienie w przeglądarce adresu http://localhost:8080/jExample/index.html pozwoli nam upewniść się, że aplikacja działa.

jExample Hello  World

To co opisałem, to zaledwie kropla w morzu możliwości Mavena, który posiada ich oczywiście dużo więcej, dzięki różnorodnym pluginom. Możemy dzięki niemu nie tylko zarządzać budowaniem projektu i zależnościami ale również generować dokumentację czy nawet sprawdzać styl kodowania.

Opublikowany przez Michał Mech

2009-04-08 o 21:19:00

Reguły nawigacji w deskryptorze stron Seam’a

brak komentarzy

We wprowadzeniu do dokumentacji frameworka JBoss Seam twórcy podkreślają, że uciekają jak tylko się da od konfiguracji za pomocą plików XML na rzecz przekazywania informacji do kontenera za pomocą adnotacji. Nie mniej jednak każdy projekt napisany z pomocą Seam’a zawiera kilka plików konfiguracyjnych, jednym z nich jest deskryptor stron: pages.xml.

W pliku deskryptora (znajduje się on wewnątrz folderu WEB-INF) mamy możliwość definiowania naturalnych konwersacji (ang. natural conversation), stron błędów dla wyjątków (ang. exception handling) oraz zasad nawigacji dla stron (ang. page navigation). Ja skupię się na nawigacji.

Pusty deskryptor (zawierający jedynie węzeł główny) wygląda następująco:

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.com/products/seam/pages
        http://jboss.com/products/seam/pages-2.1.xsd">
</pages>

W deskryptorze możemy umieścić dowolną ilość węzów <page>, które stanowią definicję stron. Podstawowa definicja strony to podanie jej identyfikatora. Definicja taka wiele nie wnosi więc podam od razu przykład jak zdefiniować akcję dla strony (ang. page action):

<page view-id="/testPage.xhtml" action="#{testComponent.sampleAction}"/>

Tak podana definicja spowoduje wykonanie akcji sampleAction komponentu testComponent tuż przed wyrenderowaniem strony testPage. Oczywiście zawsze istnieje możliwość wykonania kilku akcji (także z różnych komponentów) na okoliczność żądania strony samplePage.xhtml:

<page view-id="/testPage.xhtml">
    <action execute="#{testComponent.sampleAction}"/>
    <action execute="#{testComponent.sayHello}"/>
</page>

Wykonanie akcji możemy obwarować warunkiem logicznym (zapisanym oczywiście za pomocą języka EL). Służy do tego opcjonalny atrybut if, na przykład:

<page view-id="/testPage.xhtml">
    <action execute="#{testComponent.sampleAction}"/>
    <action if="#{identity.isLoggedIn}" execute="#{testComponent.sayHello}"/>
</page>

Akcje wykonywane są w kolejności zapisania ich w pliku. Ważne jest to by pamiętać o tym, że po każdej akcji realizowane są reguły nawigacji dla strony, co oznacza, że koniec końców nie każda akcja może mieć szanse uruchomienia. A wspomniane reguły definiuje się następująco:

<page view-id="/testPage.xhtml">
    <action execute="#{testComponent.sayHello}"/>
    <action execute="#{testComponent.sayHi}"/>

    <navigation>
        <rule if-outcome="hello">
            <render view-id="/home.xhtml"/>
        </rule>
        <rule if-outcome="hi">
            <redirect view-id="/login.xhtml"/>
        </rule>
    </navigation>
</page>

Każda akcja może zwrócić wartość, wynik (ang. outcome). To właśnie ta wartość posłuży to testowania zasad nawigacji dla strony. Niech komponent testComponent, do którego odwołania występują w przykładach wygląda tak:

@Name("testComponent")
public class TestComponent {
    @Logger private Log log;

    public String sayHello() {
        log.info("akcja sayHello zwraca outcome hello");
        return "hello";
    }

    public String sayHi() {
        log.info("akcja sayHi zwraca outcome hi");
        return "hi";
    }
}

Prześledźmy sytuację. Jeśli zażądamy strony testPage.xhtml, wykonana zostanie akcja sayHello(), której outcome to hello. W tej sytuacji wyrenderowana zostanie strona home.xhtml, co zmieni podmiot odwołania i akcja sayHi() nie będzie miała okazji się uruchomić. Wystarczy jednak zmienić kolejność akcji w pliku aby nastąpiło przekierowanie na stronę logowania – login.xhtml. Osiągniemy to jeśli najpierw wykona się sayHi():

<page view-id="/testPage.xhtml">
    <action execute="#{testComponent.sayHi}"/>
    <action execute="#{testComponent.sayHello}"/>

    <!-- ... -- >
</page>

Podobnie jak w przypadku akcji, reguły nawigacji możemy również obwarować kilkoma obostrzeniami. Przede wszystkim możemy uzależnić wszystkie reguły nawigacji od tego czy to konkretna akcje została wykonana:

<page view-id="/testPage.xhtml">
    <!-- ... -- >

    <navigation from-action="#{testComponent.sayHi}">
        <rule if-outcome="hello">
            <render view-id="/home.xhtml"/>
        </rule>
        <rule if-outcome="hi">
            <redirect view-id="/login.xhtml"/>
        </rule>
    </navigation>
</page>

Powyższy zapis sprawi, że reguły nawigacji zostaną uwzględnione tylko po wykonaniu akcji sayHi(). To pozwala na zdefiniowanie kilku bloków nawigacji, zależnych od wykonywanych akcji. Dodatkowo możemy w atrybucie evaluate dla elementu navigation umieścić dowolne wyrażenie (EL), które ma być wykonane.

Do każdej z zasad nawigacji możemy dodać warunek logiczny, który zostanie sprawdzony tuż obok sprawdzania outcome. Na przykład:

<rule if="#{identity.isLoggedIn}" if-outcome="hello">
    <render view-id="/home.xhtml"/>
</rule>

Deskryptor pages.xml ma również szereg innych możliwości. Jedną z nich jest możliwość nakładania restrykcji na strony. Możemy żądać bycia zalogowanym oraz dodać metodę sprawdzającą dowolną inną rzecz, zdefiniowaną w zewnętrznym komponencie:

<page view-id="/testPage.xhtml" login-required="false">
    <restrict>#{testComponent.checkMe()}</restrict>
</page>

W przypadku niespełnienia jednego z tych warunków zostanie wyrzucony wyjątek org.jboss.seam.security.NotLoggedInException.

UntitledW zasadzie to wszystko co dotyczy nawigacji. Na początku wspomniałem, że pominę konwersacje i skupię się na samym aspekcie nawigacji należy jednak dodać, że pomijając definiowanie naturalnych konwersacji deskryptor daje nam spore możliwości manipulowania konwersacjami wewnątrz mechanizmu nawigacji. Możemy rozpoczynać konwersacje, kończyć je, łączyć z innymi i nie tylko. Możemy również określać parametry stron, reguły ich przekazywania oraz zgłaszać wyjątki czy rozpoczynać lub kończyć zadania w ramach jBPM. Dostępnych jest szereg możliwości, o których na pewno poinformuje nas Eclipse a które ja po zgłębieniu być może opiszę :-)

Na koniec mała podpowiedź, którą pokazuje zamieszczony powyżej zrzut ekranu. Duży projekt, zwierający wiele stron i znaczną ilość definicji bardzo rozepchałby deskryptor stron. Aby zwiększyć czytelność i przejrzystość konfiguracji mamy możliwość zdefiniowana dla każdej strony oddzielnego pliku o nazwie *.page.xml, gdzie * oznacza identyfikator strony. W przykładach posługiwałem się stroną testPage, plik dla tej strony nazwyałby się testPage.page.xml

Opublikowany przez Michał Mech

2009-03-01 o 15:41:00

Kategorie Java, Programowanie

Tagi