niedziela, 29 czerwca 2008

Spring modules cache

Witam ponownie.
Pamiętacie aplikację akwarium? Aplikacja to trochę słowo na wyrost, bo w zasadzie stanowi ją jedna strona, która wylicza ile rybek pluska się w akwarium.

Wszystko działa, ale wyliczanie trochę trwa. Zaraz się tym zajmiemy, korzystając z dodatku do Springa: Spring Modules.
Spring Modules wprowadza między innymi mechanizm pozwalający na zapamiętywanie w pamięci podręcznej wyników wywołań wybranych metod (method cache). W przypadku, gdy któraś z funkcji wykonuje się długo, a dla zadanych parametrów wejściowych jej parametry wyjściowe są zawsze takie same, możemy wyniki jej wykonania przechować w pamięci podręcznej.
Trzeba jednak, trochę uważać. Przy wykorzystaniu mechanizmu method cache, tylko wejście i wyjście danej funkcji się zgadza. Wywołanie funkcji zostaje pominięte, natomiast rezultat jest zwracany z pamięci podręcznej.
Co ciekawe, te wszystkie czary można uruchomić bez napisania jakiejkolwiek linijki kodu w jawie. Trzeba tylko zmienić konfigurację. Ale do rzeczy...
Po uruchomieniu aplikacji akwarium i wpisaniu adresu strony:
http://localhost:8080/aquarium/info.htm
możemy zauważyć, że strona ładuje się kilka sekund. W dodatku zawsze zwraca inny wynik, coś ok 200000.



Odpowiedzialna jest za to klasa

package pl.matt.aquarium.manager.impl;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class AquariumManagerImpl implements AquariumManager {

/**
* pojemność akwarium w centymetrach sześciennych
*/
private int cubicCentimetres;
/**
* minimalna gęstość rybki
*/
private int minimalDensity;;

@Override
public int getFishCount() {
int count = 0;
for (int index = 0; index < cubicCentimetres; index++) {
if (getDensity(index) > minimalDensity) {
count++;
}
}
return count;
}

/**
* @param centimetreIndex
* @return gęstość wybranego fragmentu akwarium
*/
private int getDensity(int centimetreIndex) {
return (int) (Math.random() * 100);
}

public void setCubicCentimetres(int cubicCentimetres) {
this.cubicCentimetres = cubicCentimetres;
}

public void setMinimalDensity(int minimalDensity) {
this.minimalDensity = minimalDensity;
}

}

a dokładniej jej metoda public int getFishCount().

Pierwsza rzecz, jaką trzeba zrobić, aby dodać Spring Modules do naszego projektu to dodanie nowego repozytorium z którego skorzysta maven2. W tym celu trzeba wprowadzić kilka zmian w pliku settings.xml. Zazwyczaj jest on w katalogu ~/.m2/ lub /etc/maven2/. W sekcji profiles trzeba dopisać nowy profil:

<profiles>
<profile>
<id>standard-extra-repos</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>


Następnie dodaję kilka nowych zależności do pliku pom.xml

<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>apache-oro</groupId>
<artifactId>jakarta-oro</artifactId>
<version>2.0.8</version>
</dependency>


Cały plik pom.xml wygląda następnująco:

<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>pl.matt</groupId>
<artifactId>aquarium</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>aquarium Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
</dependency>
<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>apache-oro</groupId>
<artifactId>jakarta-oro</artifactId>
<version>2.0.8</version>
</dependency>
</dependencies>
<build>
<finalName>aquarium</finalName>
<directory>/tmp/target</directory>
<outputDirectory>/tmp/target/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>


generalnie nic ciekawego. Czas wziąć na warsztat serce i mózg aplikacji springowej. W moim przypadku jest to plik springapp-servlet.xml.

Dodaję ziarna cacheManager i cacheProviderFacade określając położenie pliku konfiguracyjnego ehcache.xml.

<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
<bean id="cacheProviderFacade" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
<property name="cacheManager" ref="cacheManager" />
</bean>


Ziarno cachingInterceptor pozwala wskazać metody, których wyniki wywołań będą przechowywane w pamięci podręcznej. Wskazuję metody zaczynające się od liter get interfejsu pl.matt.aquarium.manager.AquariumManager. Czyli de facto tylko metodę getFishCount().

<bean id="cachingInterceptor"
class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingModels">
<props>
<prop key="pl.matt.aquarium.manager.AquariumManager.get*">cacheName=testCache</prop>
</props>
</property>
</bean>

Wyniki będą przechowywane we fragmencie pamięci nazwanym testCache. Zaraz go zdefiniuję, ale najpierw jeszcze jedno ziarno konfigurujące przechowywanie wyników wywołań metod w pamięci podręcznej:

<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<idref local="aquariumManager" />
</list>
</property>
<property name="interceptorNames">
<list>
<value>cachingInterceptor</value>
</list>
</property>
</bean>


Cały plik springapp-servlet.xml wygląda tak:

<?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">
<!--
the application context definition for the springapp DispatcherServlet
-->
<bean name="/info.htm" class="pl.matt.aquarium.InfoController"
autowire="byName" />

<!-- freemarker config -->
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/freemarker/" />
</bean>
<!--
View resolvers can also be configured with ResourceBundles or XML
files. If you need different view resolving based on Locale, you have
to use the resource bundle resolver.
-->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<!--
if you want to use the Spring FreeMarker macros, set this property to
true
-->
<property name="exposeSpringMacroHelpers" value="true" />
</bean>
<bean id="aquariumManager" class="pl.matt.aquarium.manager.impl.AquariumManagerImpl">
<property name="cubicCentimetres" value="20000000" />
<property name="minimalDensity" value="98" />
</bean>

<!-- cache -->
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
<bean id="cacheProviderFacade" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
<property name="cacheManager" ref="cacheManager" />
</bean>
<bean id="cachingInterceptor"
class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingModels">
<props>
<prop key="pl.matt.aquarium.manager.AquariumManager.get*">cacheName=testCache</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<idref local="aquariumManager" />
</list>
</property>
<property name="interceptorNames">
<list>
<value>cachingInterceptor</value>
</list>
</property>
</bean>
</beans>


Pozostaje jeszcze skonfigurować Ehcache, który będzie u mnie zarządzał pamięcią podręczną. Plik ehcache.xml tworzę w katalogu resources.

<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir/cache" />

<defaultCache
maxElementsInMemory="9999"
eternal="false"
timeToIdleSeconds="777"
timeToLiveSeconds="777"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="600"
memoryStoreEvictionPolicy="LRU"
/>

<cache name="testCache"
maxElementsInMemory="9999"
eternal="false"
timeToIdleSeconds="777"
timeToLiveSeconds="777"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="600"
memoryStoreEvictionPolicy="LRU"
/>

</ehcache>


Plik zawiera konfigurację dwóch fragmentów pamięci podręcznej. Nie wchodząc w szczegóły, konfiguracja fragmentu defaultCache jest obowiązkowa. Natomiast testCache będzie pamiętał wywołania metody getFishCount().
I to tyle. Czas sprawdzić, czy działa. Buduję aplikację, wchodzę na stronę:
http://localhost:8080/aquarium/info.htm
Ładuje się długo. Wchodzę ponownie. Tym razem strona załadowała się błyskawicznie. W dodatku liczba rybek pozostała niezmieniona. Miodzio - i to wszystko bez ani jednej linijki kodu w jawie. Całą aplikację akwarium znajdziesz tutaj.

Oczywiście do konfiguracji mechanizmu method cache można wykorzystać adnotacje, a zamiast Ehcache, JBoss Cache, Java Caching System (JCS) albo OSCache. Takie uroki Springa.

środa, 25 czerwca 2008

Ganymede, bugi i tag <pre />

No to doczekaliśmy się finalnego wydania najnowszego Eclipsa 3.4.0 Ganymede.


Co ma nowego nie do końca wiem, ale ma jakieś cieplejsze logo i nie zamula po starcie. W wersji 3.3.x świeżo po starcie IDE i kliknięciu w jakiekolwiek menu miałem 10 sekundową przerwę. Teraz mogę od razu zabierać się do pracy.

Zabieram się zatem do pracy, w której nigdy nie byłem znawcą HTMLa. Jeżeli w ogóle mogłem uchodzić za jakiegokolwiek znawcę, to chyba tylko tenisa stołowego i piłkarzyków również stołowych, czym się zresztą w pracy nie zajmuję. Niemniej coś tam o HTMLu wiedziałem i wraz z upływem czasu nabierałem przeświadczenia, że HTML mnie już niczym nie zaskoczy...
Tym większe było moje zdziwienie, kiedy to zabrałem się do pisania bloga i wklejania doń kawałków kodu java, xml czy jakiegokolwiek innego. Kod ulegał po prostu spłaszczeniu, mniej więcej tak:


public static void main(String[] args){
System.out.println("ole");
}


Tak nie może być... Krótkie spojrzenie na blogi z listy Top Trendy nie pozostawiło wątpliwości. Można sobie z tym poradzić. Uruchomienie FireBuga poskutkowało odkryciem nieznanego mi znacznika HTML <pre />.

Rzut okiem na dokumentację HTMLa nie pozostawia wątpliwości. To jest to, czego szukałem:
The pre element defines preformatted text. The text enclosed in the pre element usually preserves spaces and line breaks. The text renders in a fixed-pitch font.
Umieszczam powyższy fragment wewnątrz znaczcnika <pre />

public static void main(String[] args){
System.out.println("ole");
}


Od razu lepiej.

Ale, żeby nie było za dobrze... Nowy Eclipse to nowe bugi. Dziś odkryłem znikające zamknięte projekty ze zbiorów roboczych (workking set). Brzmi trochę jak niezidentyfikowany obiekt latający... ale do rzeczy.

Jeżeli w zbierze roboczym mam projekty zamknięte i otwarte (a zazwyczaj tak jest)


przystąpię do edycji zawartości zbioru (Edit Active Working Set... z menu widoku Package Explorer...) i dodam nowe zamknięte projekty


to po kliknięciu przycisku Finish) stare zamknięte projekty znikają ze zbioru.


Projekt aima-java zniknął. Zostały tylko te świeżo dodane. Nieładnie...

środa, 18 czerwca 2008

pierwsze kroki z Java Native Interface

Jako, że stałem się szczęśliwym posiadaczem IntelliJ IDEA przyszedł czas na jego wypróbowanie. Nigdy nie przepadałem za interfejsem użytkownika pisanym w SWINGu, a takowy posiada to IDE, ale może czas się do niego przekonać. W końcu wychodzi na to, że szansa na popularne IDE z interfejsem w SWINGu jest taka sama, jak na prezydenta Polski o imieniu Lech (66%).



Przy okazji próbowania się z IntelliJ IDEA zmierzę się też z Java Native Interface. Generalnie rzecz ujmując JNI pozwala wywołać z poziomu kodu Java funkcje ze skompilowanych do kodu natywnego bibliotek napisanych w C, C++ i Asemblerze. Nie można co prawda wywołać dowolnej funkcji z natywnej biblioteki, tylko funkcję o wygenerowanej przez Javę sygnaturze, ale już w ciele tej funkcji możemy wywoływać co chcemy. Reasumując przebieg wydarzeń wygląda następująco:

1. Piszemy metodę w kodzie Java o sygnaturze native
2. Generujemy sygnaturę tej metody w C/C++ (nie wiem, co z assemblerem)
3. Piszemy ciało metody o wygenerowanej sygnaturze, tu jeżeli jest taka potrzeba, wywołujemy w nim dowolne metody z bibliotek napisanych w C/C++

Co daje JNI? Niektórzy twierdzą, że większą wydajność, ale spory, czy Java jest mniej wydajna od C/C++ są burzliwe, a do tego dochodzi narzut na wywołanie metody przez JNI, więc na główną zaletę ta cecha nie pasuje.
Istnieje niestety ciągle wiele bibliotek napisanych w C/C++ które nie mają i pewnie długo jeszcze nie będą miały odpowiedników w Javie i tu moim zdaniem pole do popisu dla JNI. Mnie przynajmniej po to skorzystanie z JNI było potrzebne.
Wady JNI? Jak się zaraz przekonacie... Jakikolwiek błąd w kodzie biblioteki powoduje wywalenie całej wirtualnej maszyny, bez czytelnych komunikatów błędu. Generalnie nie wiadomo co się dzieje. Jak nie trzeba, lepiej się w JNI StraBe nie pakować.
Ale do rzeczy...

Tworzę nowy projekt o nazwie Jni



Oprócz katalogu src utworzę w nim katalog c na pliki źródłowe w kodzie C.

Zaczynam od stworzenia klasy pl.matt.jni.HelloJNI


package pl.matt.jni;

/**
* Mateusz Zięba
* 2008-06-19 02:27:56
*/
public class HelloJNI {


private static native void say(String greeting);

static {
System.load("/home/mateusz/priv/IdeaProjects/Jni/c/hello.so");
}

public static void main(String args[]) {
say("Ala ma kota");
}
}


Warto zauważyć tutaj 2 rzeczy:
metodę natywną private static native void say(String greeting), która zostanie zaimplementowana w bibliotece napisanej w języku C,
oraz blok

static {
System.load("/home/mateusz/priv/IdeaProjects/Jni/c/hello.so");
}

gdzie ni mniej ni więcej tylko ta biblioteka napisana w C, której jeszcze nie ma, ale która zaraz będzie, zostaje załadowana przez JVM.

To by było tyle w Javie.
Wygeneruję teraz sygnaturę metody. W tym celu przechodzę do katalogu ze skompilowanymi klasami... Niestety taki nie istnieje. Klikam więc prawym przyciskiem myszy (PPM) na klasie HelloJNI i z menu kontekstowego wybieram pozycję Compile 'HelloJNI'. Pojawił się katalog out. W nim podkatalogi test i production. Trochę nie rozumiem tej struktury katalogów generowanej przez IntelliJ IDEA, ale może kiedyś zrozumiem. W katalogu out/production/Jni generuję plik nagłówkowy C.

javah -jni pl.matt.jni.HelloJNI

brak komunikatu oznacza sukces. W bieżącym katalogu pojawił się plik pl_matt_jni_HelloJNI.h, który przenoszę do katalogu c

mv pl_matt_jni_HelloJNI.h ../../../c/

W dużym projekcie to wszystko oraz następne rzeczy robiłby oczywiście makefile, ale jakoś sobie bez niego tym razem poradzę.
plik pl_matt_jni_HelloJNI.h wygląda tak:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class pl_matt_jni_HelloJNI */

#ifndef _Included_pl_matt_jni_HelloJNI
#define _Included_pl_matt_jni_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: pl_matt_jni_HelloJNI
* Method: say
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_pl_matt_jni_HelloJNI_say
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif


na jego podstawie tworzę plik hello.c

#include "pl_matt_jni_HelloJNI.h"
#include <jni.h>
#include <stdio.h>

JNIEXPORT void JNICALL Java_pl_matt_jni_HelloJNI_say
(JNIEnv * env, jclass class, jstring jstr) {
jboolean iscopy;
char* greeting = (*env)->GetStringUTFChars(env, jstr, &iscopy);
printf(greeting);
}


Nie rozwodząc się, ta funkcja napisana w C, skonwertuje przekazany z kodu Java parametr na ciąg znaków char*
char* greeting = (*env)->GetStringUTFChars(env, jstr, &iscopy);
i wyświetli go na ekranie. Mam nadzieję.

Kompiluję plik hello.c

mateusz@mateusz-laptop:~/priv/IdeaProjects/Jni/c$ gcc -I"/usr/lib/jvm/java-6-sun/include"
-I"/usr/lib/jvm/java-6-sun/include/linux" -c hello.c -o hello.o
hello.c: W funkcji `Java_pl_matt_jni_HelloJNI_say,:
hello.c:8: ostrzeżenie: inicjalizacja odrzuca kwalifikatory z typu docelowego wskaźnika


Jakieś ostrzeżenie... nie znam się za bardzo na programowaniu w C więc nic nie jestem w stanie z nim zrobić. Jedziemy dalej.
Tworzę bibliotekę hello.so:

gcc -shared -o hello.so hello.o


Właśnie do tego pliku hello.so odwołuję się w poliku HelloJNI w sekcji

static {
System.load("/home/mateusz/priv/IdeaProjects/Jni/c/hello.so");
}


Tym razem bez ostrzeżenia.
Uff... To koniec. Odpalam klasę HelloJNI

/usr/lib/jvm/java-6-sun/bin/java -Didea.launcher.port=7532 -Didea.launcher.bin.path=/opt/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-6-sun/jre/lib/plugin.jar:/usr/lib/jvm/java-6-sun/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun/jre/lib/javaws.jar:/usr/lib/jvm/java-6-sun/jre/lib/deploy.jar:/usr/lib/jvm/java-6-sun/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun/jre/lib/management-agent.jar:/usr/lib/jvm/java-6-sun/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-6-sun/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-6-sun/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-6-sun/jre/lib/ext/dnsns.jar:/home/mateusz/priv/IdeaProjects/Jni/out/production/Jni:/opt/idea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain pl.matt.jni.HelloJNI
Ala ma kota
Process finished with exit code 0


Ala ma kota, czyli wszystko działa. Napis został wyświetlony w języku C a nie w Javie.

Na koniec mały bug z IntelliJ IDEA - okienko do zmiany nazwy pliku hello.c.



Tram po środku są literki - nowa nazwa pliku.

PS. znacie jakiś dobry synonim słowa "natywny"?

niedziela, 15 czerwca 2008

dekompilacja kodu Java w locie i w Eclipsie

Dzisiaj kontynuuję moje przygody z najnowszym Eclipse Ganymede. Zainstaluję do niego wtyczkę (w końcu to Eclipse) która mam nadzieję rozwiąże przynajmniej część moich problemów z brakującymi źródłami zewnętrznych bibliotek. O ile źródła wielu z nich, jak np. Spring są łatwe do znalezienia, o tyle żeby znaleźć źródła JavaMail musiałem się kiedyś nieźle na pocić. Czasami nie mam na to czasu ani ochoty, a jakieś źródła fajnie zobaczyć.
Stąd mój zwrot w kierunku dekompilacji kodu i programu Jad - the fast JAva Decompiler do którego istnieje wtyczka JadClipse przeznaczona dla środowiska Eclipse. Kod po dekompilacji zawsze będzie gorszy niż pierwotny kod źródłowy. Nie będzie komentarzy ani adnotacji ale lepszy rydz niż nic.
Zatem do dzieła.
Pobieram Jad 1.5.8e for Linux on Intel platform
rozpakowuję do katalogu /opt/jad i nadaję prawa do wykonania pliku jad
mateusz@mateusz-laptop:~$ chmod +x /opt/jad/jad

krótki sprawdzian, czy działa:

mateusz@mateusz-laptop:~$ /opt/jad/jad
/opt/jad/jad: error while loading shared libraries: libstdc++-libc6.2-2.so.3: cannot open shared object file: No such file or directory

i tu klops, nie działa... Niespełnione zależności, czyli coś co w Linuksie lubię najbardziej.
Wkładam gogle, martwa cisza. W Ubuntu 8.04 nikt się nie chwali, że to rozwiązał. Ja się pochwalę. Pobieram i instaluję paczkę libstdc++2.10-glibc2.2 dla Ubuntu 7.10. O dziwo zainstalowała się bez przeszkód. Sprawdzam czy działa Jad:
mateusz@mateusz-laptop:~$ /opt/jad/jad
Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov (kpdus@yahoo.com).
Usage: jad [option(s)] <filename(s)>
Options: -a - generate JVM instructions as comments (annotate)
-af - output fully qualified names when annotating
-b - generate redundant braces (braces)
-clear - clear all prefixes, including the default ones
-d <dir> - directory for output files
-dead - try to decompile dead parts of code (if there are any)
-dis - disassembler only (disassembler)
-f - generate fully qualified names (fullnames)
-ff - output fields before methods (fieldsfirst)
-i - print default initializers for fields (definits)
-l<num> - split strings into pieces of max <num> chars (splitstr)
-lnc - output original line numbers as comments (lnc)
-lradix<num>- display long integers using the specified radix
-nl - split strings on newline characters (splitstr)
-noconv - don't convert Java identifiers into valid ones (noconv)
-nocast - don't generate auxiliary casts
-noclass - don't convert .class operators
-nocode - don't generate the source code for methods
-noctor - suppress the empty constructors
-nodos - turn off check for class files written in DOS mode
-nofd - don't disambiguate fields with the same names (nofldis)
-noinner - turn off the support of inner classes
-nolvt - ignore Local Variable Table entries (nolvt)
-nonlb - don't insert a newline before opening brace (nonlb)
-o - overwrite output files without confirmation
-p - send all output to STDOUT (for piping)
-pa <pfx>- prefix for all packages in generated source files
-pc <pfx>- prefix for classes with numerical names (default: _cls)
-pe <pfx>- prefix for unused exception names (default: _ex)
-pf <pfx>- prefix for fields with numerical names (default: _fld)
-pi<num> - pack imports into one line using .* (packimports)
-pl <pfx>- prefix for locals with numerical names (default: _lcl)
-pm <pfx>- prefix for methods with numerical names (default: _mth)
-pp <pfx>- prefix for method parms with numerical names (default:_prm)
-pv<num> - pack fields with the same types into one line (packfields)
-r - restore package directory structure
-radix<num>- display integers using the specified radix (8, 10, or 16)
-s <ext> - output file extension (default: .jad)
-safe - generate additional casts to disambiguate methods/fields
-space - output space between keyword (if, while, etc) and expression
-stat - show the total number of processed classes/methods/fields
-t<num> - use <num> spaces for indentation (default: 4)
-t - use tabs instead of spaces for indentation
-v - show method names while decompiling

Teraz lepiej.
Otwieram Eclipse a w nim ostatnio stworzony projekt aquarium.
Przechodzę do klasy InfoController. Klikam myszką na nazwę interfejsu Controller i naciskam klawisz F3



Source not found czyli źródła niepodpięte.
Czas zainstalować wtyczkę JadClipse. Pobieram jadclipse3.3 i kopiuję do katalogu plugins Eclipse Ganymede. Restart IDE.
W menu Window/Preferences... pojawiła się pozycja Java/JadClipse czyli wtyczka wydaje się, że działa. Wskazuję położenie pliku wykonywalnego jad na /opt/jad/jad


Ponownie naciskam F3 kiedy mam zaznaczoną nazwę interfejsu InfoController.


Widzę kod, czyli działa. Przechodzę dalej do klasy ModelAndView


No, pełen sukces.

poniedziałek, 9 czerwca 2008

Eclipse Ganymede i kompilowanie javy do kodu natywnego

Czołem!

Zapewne zauważyłeś na kilku blogach wysyp wpisów o nowej wersji środowiska Eclipse Ganymede. Dzieje się tak dla tego, że jesteśmy łasi na koszulki, które Eclipse obiecuje w konkursie Ganymede Around the World Contest. Nie inaczej jest ze mną. Zobaczymy, co z tego wyjdzie.

Tak więc dziś postanowiłem się zmierzyć z tematem, który czasami pojawia się na łamach grupy dyskusyjnej pl.comp.lang.java i który zawsze uważałem za ślepą uliczkę rozwoju tego języka. Chodzi o kompilację kodu źródłowego Javy do kodu natywnego systemu operacyjnego.

Plusy tego rozwiązania:
- nie potrzeba JRE
- wyższa wydajność

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

O ile brak konieczności instalacji JRE jak i tracona przenośność są raczej bezsprzeczne o tyle do rozstrzygnięcia pozostaje kwestia wydajności, którą się dzisiaj zajmę.

Kod źródłowy będę pisał oczywiście przy pomocy najnowszego Eclipse Ganymede. Uruchamiał i kompilował będę javą 1.6. Kompilację do kodu natywnego przeprowadzę z wykorzystaniem gcj 4.2.3.

Najpierw zainstaluję gcj. Mam Ubuntu, więc wystarczy:
mateusz@mateusz-laptop:~$ sudo apt-get install gcj

sprawdzenie co mam zainstalowane:

mateusz@mateusz-laptop:~$ java -version
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) Server VM (build 10.0-b22, mixed mode)
mateusz@mateusz-laptop:~$ gcj --version
gcj (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu6)
Copyright (C) 2007 Free Software Foundation, Inc.
To jest wolne/otwarte oprogramowanie; zobacz kod źródłowy aby zapoznać sie
z warunkami kopiowania. Nie ma ŻADNEJ gwarancji; nawet na MOŻLIWOŚĆ SPRZEDAŻY
ani na ODPOWIEDNIŚĆ DO KONKRETNEGO CELU.

Pobieram też Eclipse Ganymede.
Przed uruchomieniem tworzę skrót w pasku szybkiego uruchamiania i tu niespodzianka... W archiwum eclipse-jee-ganymede-RC2-linux-gtk.tar.gz Nie ma ikonki Eclipse, albo ja nie umiem jej znaleźć. Posłużę się ikonką eclipse.xpm z wersji 3.3 tego środowiska.

Odpalam Eclipse:



Niespodzianka numer 2:



Eclipse chce wysyłać informacje o moich wtyczkach. Przez chwile czuję się jak na Windowsie, po czym zaznaczam opcję, żeby nic nie wysyłał.
Tworzę projekt native i klasę pl.matt.gcj.TestClass


package pl.matt.gcj;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* @author mateusz
*
*/
public class TestClass {

/**
* @param args
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
List list = new ArrayList();
for (byte i = 0; i < 300; i++) {
list.add(list.size());
if (list.size() > 10000000) {
break;
}
}

Collections.shuffle(list);
double avg = 0;

for (Integer integer : list) {
avg += (double)integer/list.size();
}

long stop = System.currentTimeMillis();
System.out.println(stop-start + " ms.");
}

}

Do listy wstawiam 10000001 elementów. Listę mieszam i liczę średnią. Używam automatycznej konwersji int do Integer (autoboxng). GCJ podobno radzi sobie z kodem źródłowym zgodnym z Javą 1.5. Zobaczymy.
Kompiluję kod za pomocą gcj:

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj -o /tmp/gcj/a.out pl/matt/gcj/TestClass.java
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status

rzut okiem na FAQ GCJ i już wiem, że trzeba podać położenie metody main()

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj --main=pl.matt.gcj.TestClass
-o /tmp/gcj/a.out pl/matt/gcj/TestClass.java

poszło... Został utworzony plik /tmp/gcj/a.out
Czas na testy. Ponieważ czas wykonywania programu w Javie nie uwzględnia czasu uruchomienia samej wirtualnej maszyny Java, tworzę 2 proste skrypty w BASHu:
a.sh który 5 razy uruchomi plik zawierający kod natywny a.out

mateusz@mateusz-laptop:/tmp/gcj$ cat a.sh
#!/bin/bash
START=$(date +%s)
./a.out
./a.out
./a.out
./a.out
./a.out
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

oraz j.sh, który uruchomi TestClass za pomocą VM Java.

mateusz@mateusz-laptop:/tmp/gcj$ cat j.sh
#!/bin/bash
START=$(date +%s)
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

Testy nie pozostawiają wątpliwości:

mateusz@mateusz-laptop:/tmp/gcj$ ./a.sh
8630 ms.
8299 ms.
8290 ms.
8380 ms.
8321 ms.
43 sekund
mateusz@mateusz-laptop:/tmp/gcj$ ./j.sh
5671 ms.
5824 ms.
5913 ms.
5906 ms.
5823 ms.
29 sekund

Co ciekawe czas uruchomienia wirtualnej maszyny Java jest praktycznie pomijalny.
Reasumując:

Plusy kompilowania aplikacji Java do kodu natywnego
- nie potrzeba JRE

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

A może ty stosujesz na co dzień kompilację programów napisanych w języku Java do kodu natywnego? Jeżeli tak, napisz dlaczego i jakie są tego zalety, których nie widzę.

sobota, 7 czerwca 2008

wiosenna fasolka

Niniejszy wpis dedykuję panu Stefanowi Białasowi. Stefan Białas został w maju trenerem Jagiellonii Białystok. Dzisiaj został zwolniony z pracy. Czyżby dla tego, że drużyna słabo grała? Nie. Jagiellonia w tym czasie nie rozegrała żadnego spotkania.


To tyle, jeśli chodzi o didaskalia. Dzisiaj do naszego akwarium wrzucimy wiosenną fasolkę. Niby już lato, ale tak naprawdę, do aplikacji stworzonej we wpisie Spring MVC + Freemarker dodamy springowe ziarno (bean). Po co? No generalnie zaletą springowych ziaren jest to, że mogą one korzystać z dobrodziejstw springa, z których skorzystamy kiedyś w przyszłości. Niedalekiej. Poza tym stworzymy kolejną warstwę abstrakcji - warstwę biznesową. Same plusy.


Do rzeczy.
Aplikacja akwarium wyświetla ilość rybek w akwarium. Ilość ta jest obliczana w klasie InfoController. Dobrze byłoby odseparować wyliczanie tej wartości od klasy kontrolera i przenieść do warstwy biznesowej, której jeszcze nie mamy.
Tworzę zatem 2 paiety: pl.matt.aquarium.manager i pl.matt.aquarium.manager.impl. W pakiecie pl.matt.aquarium.manager będą biznesowe (akwarystyczne?) interfejsy, w pl.matt.aquarium.manager.impl ich implementacje.

Tworzę interfejs AquariumManager


package pl.matt.aquarium.manager;

/**
* @author mateusz
*
*/
public interface AquariumManager {

/**
* @return ilość rybek
*/
public int getFishCount();

}

zawiera on jedną metodę, która zwraca ilość rybek jakie pluskają w akwarium.

Tworzę też domyślną implementację powyższego interfejsu AquariumManagerImpl:


package pl.matt.aquarium.manager.impl;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class AquariumManagerImpl implements AquariumManager {

/**
* pojemność akwarium w centymetrach sześciennych
*/
private int cubicCentimetres;
/**
* minimalna gęstość rybki
*/
private int minimalDensity;;

@Override
public int getFishCount() {
int count = 0;
for (int index = 0; index < cubicCentimetres; index++) {
if (getDensity(index) > minimalDensity) {
count++;
}
}
return count;
}

/**
* @param centimetreIndex
* @return gęstość wybranego fragmentu akwarium
*/
private int getDensity(int centimetreIndex) {
return (int) (Math.random() * 100);
}

public void setCubicCentimetres(int cubicCentimetres) {
this.cubicCentimetres = cubicCentimetres;
}

public void setMinimalDensity(int minimalDensity) {
this.minimalDensity = minimalDensity;
}

}


Implementacja jest ciekawsza. Jest tam parametr cubicCentimetres;
który mówi o ilości centymetrów sześciennych (pojemności) akwarium. Co ciekawe, parametr nie jest zainicjalizowany w klasie, ale ma metodę modyfikującą (tzw. setter), więc jest nadzieja, że jednak jakoś będzie ustawiony. Ilość rybek nie zostanie już wylosowana, tylko wyliczona. Dla każdego centymetra sześciennego akwarium liczona jest jego gęstość (metoda private int getDensity(int centimetreIndex)), jeżeli jest większa niż minimalDensity (który również nie jest zainicjalizowany) uznaje się, że akurat tutaj mamy rybkę i licznik jest zwiększany.
Jak masz w akwarium duże ryby, mogą być policzone wielokrotnie. Zakładam, że masz ławicę neonków.


Teraz serce aplikacji springowej, czyli plik konfiguracyjny. W moim przypadku jest to springapp-servlet.xml.
Definiuję nowe ziarno:

<bean id="aquariumManager"
class="pl.matt.aquarium.manager.impl.AquariumManagerImpl">
<property name="cubicCentimetres" value="20000000" />
<property name="minimalDensity" value="98" />
</bean>

Definiując ziarno możemy ustawić jego stan (wartości zmiennych, co też robię. Wstrzyknę wartość tych parametrów metodami modyfikującymi (settery) ale można to zrobić też konstruktorem z parametrami.
To ziarno będzie singletonem. Domyślnie każde springowe ziarno jest singletonem.


Czas zmienić odrobinę klasę pl.matt.aquarium;.InfoController aby korzystała z logiki ukrytej za interfejsem AquariumManager


package pl.matt.aquarium;

import java.io.IOException;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class InfoController implements Controller {

private AquariumManager aquariumManager;

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

int fishCount = aquariumManager.getFishCount();
return new ModelAndView("info", "fishCount", fishCount);
}

public void setAquariumManager(AquariumManager aquariumManager) {
this.aquariumManager = aquariumManager;
}

}


tu zmiany są raczej drobne. Znowu jest jakaś niezainicjalizowana zmienna. Jak się już zapewne domyślacie, wstrzykniemy ją w pliku konfiguracyjnym springa. Piszę "wstrzykniemy" bo ten bajer to nic innego jak Dependency Injection . Czyli zainicjalizowanie wartości ziarna poza nim samym.


Modyfikuję zatem ponownie plik springapp-servlet.xml.


zmieniam

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" >

na

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" autowire="byName" />

Spodziewałeś się, że zmienię na:

<bean name="/info.htm" class="pl.matt.aquarium.InfoController">
<property name="aquariumManager">
<ref bean="aquariumManager" />
</property>
</bean>

Tak też mogło być, ale tym sposobem poznaliśmy nową opcję, trochę niebezpieczną autowire. Opcja ta próbuje automatycznie wstrzyknąć wartości zmiennych, w naszym przypadku po nazwie (zmienna się nazywa aquariumManager więc wstrzyknięty jest bean o tej samej nazwie. Jeżeli dasz autowire="byType" spring spróbuje odnaleźć i wstrzyknąć ziarno implementujące interfejs AquariumManager czyli też będzie dobrze.


OK. Czas sprawdzić, czy wszystko działa. Strona się chwilę ładuje, ale działa.


Mam teraz 199 140 rybek. A wy?

Zauważcie, że po zmianie konfiguracji springa (plik springapp-servlet.xml) nie trzeba kompilować ponownie aplikacji. Wystarczy ją uruchomić ponownie i wszystko gra.

Wszystkie pliki projektu akwarium znajdziesz w tym archiwum.

piątek, 6 czerwca 2008

Złe wieści z Bad Waltersdor

Niestety. Lekarze stwierdzili, że nie będę gotowy na 100% do gry z Niemcami. Ze łzami w oczach pakuję walizki. Euro2008 obejrzę w telewizji.

środa, 4 czerwca 2008

getClass() klasy anonimowej

Zastanawialiście się kiedyś, co zwróci metoda getClass() klasy anonimowej? Ja się zastanawiałem. Nic nie wykombinowałem, więc stworzyłem prostą klasę:


package pl.matt;

import java.util.HashSet;
import java.util.Set;

public class AnnonymousClassTest {
public static void main(String[] args) {
Set<Object> set = new HashSet<Object>() {
/**
*
*/
private static final long serialVersionUID = -2992990530694342435L;

{
add(1);
add(2);
}
};

System.out.println(set.getClass());

}
}


odpalam i co widzę?

class pl.matt.AnnonymousClassTest$1


Dociekliwi znajdą w katalogu z klasami pliki:
AnnonymousClassTest$1.class  AnnonymousClassTest.class

czyli wszystko zgodnie z planem...

wtorek, 3 czerwca 2008

instaluję(my) przeglądarkę Internet Explorer

Kolejny post z cyklu instaluję(my). Nie da się ukryć, że nie tak dawno miałem format komputera. Kiedy? Łatwo poznać wchodząc na stronę ubuntu.pl. Najnowsza wersja to 8.04.

Dziś zainstaluję Internet Explorera, pewnie szóstkę. Oczywiście nie na Windowsie, tylko na Ubuntu. Tchnęło mnie na tą wspaniałą przeglądarkę przy okazji przeglądania kodu HTML, który jest generowany przy okazji moich wpisów. Mam głębokie obawy, że mogą one być nieczytelne na przeglądarkach innych niż FireFox.

Generalnie rzecz ujmując, IE na Linuksie działa dzięki projektowi IEs4Linux.
Na w/w stronie instrukcje dotyczące Ubuntu są dość stare, więc może ten wpis na coś się komuś przyda.

Najpierw trzeba się upewnić, czy ma się zainstalowany emulator Wine.
mateusz@mateusz-laptop:~$ wine --version
preloader: Warning: failed to reserve range 00000000-00010000
wine-0.9.61

czyli wine jest zainstalowany.

Jeżeli nie jest (ujrzysz komunikat: wine: polecenie nieodnalezione) trzeba wine zainstalować:
sudo apt-get install wine


Następnie kilka kroków zgodnie z instrukcją na stronie IEs4Linux, czyli pobranie, rozpakowanie i uruchomienie programu IEs4Linux
wget http://www.tatanka.com.br/ies4linux/downloads/ies4linux-latest.tar.gz
tar zxvf ies4linux-latest.tar.gz
cd ies4linux-*
./ies4linux

Odpala się instalator graficzny, klikam Advanced i ustawiam po swojemu wszystkie ścieżki

Wybieram instalację IE 6.0, z flashem i skrótem na pulpicie. Jak w Windowsie... Klikam OK.


Coś się dzieje...


IEs4Linux installations finished!

Aby uruchomić IE wpisz:
/opt/ies4linux/bin/ie6

Czyżby miało być tak pięknie?
wpisuję:

/opt/ies4linux/bin/ie6

działa. Flash działa (pojawiło się okienko z pytaniem, czy zainstalować, kliknąłem OK). Wchodzę na tego bloga...



Tragedii nie ma, ale muszę popracować nad formatowaniem XMLi i kodu Jawowego.

Miłe zaskoczenie na koniec dnia.

Spring MVC + Freemarker

Witam. Dzisiejszy post dedykuję Panu Kazimierzowi z którym przepiłem 22100.

Specjalnie dla Pana Kazimierza zmierzymy się dziś ze Spring MVC.
Generalnie szkielety MVC dzielą się na 2 rodzaje. W jednym z każdą akcją (wysłaniem formularza) związana jest klasa Java, w drugim z każdą stroną związana jest klasa. Spring MVC należy do tych pierwszych. Do tych drugich np. JSF czy Apache Wicket.
Ma to jednak zasadniczą zaletę, strona nie jest związana ze stojącym za nią ziarnem (Java Bean) i


do jednej strony dane można wrzucać na wiele sposobów.

Niestety nie znalazłem prostego archetypu dla aplikacji webowej ze springiem, więc posłużymy się archetypem maven-archetype-webapp. Żeby było ciekawiej, tym razem nie z linii poleceń.
Zainstalowałem bowiem wtyczkę M2Eclipse do mojego Eclipsa 3.3.

1. Tworzenie aplikacji webowej

Teraz wybieram
File/New.../Project
i zaznaczam
Maven project

Następnie wybieram
Next
i zaznaczam odpowiedni archetyp, czyli maven-archetype-webapp.



Podaję Group Id na pl.matt i ArtifactId na aquarium (to będzie nazwa projektu). Wersję ustawiam na 1.0.

Klikam
Finish
i projekt gotowy.

Żeby stworzyć archiwum WAR i skompilować wszystkie klasy (których jeszcze nie ma), klikam prawym przyciskiem myszy (ppm) na projekcie, wybieram
Run As... / Maven install


Projekt zbudowany. Teraz wystarczy skopiować lub podlinkować wara z katalogu target do katalogu webapps Tomcata.
Uruchamiam Tomcata, wpisuję adres:
http://localhost:8080/aquarium/
i widzę piękne witaj świecie.

Aplikacja działa. Czas podłączyć Springa.

2. Konfiguracja SpringMVC
Aplikacja, która powstała jest bardzo prosta. Nie ma nawet żadnego serwletu. Dorzucę do niej springa. Ponieważ korzystam z mavena2 odbędzie się to przez modyfikację pliku pom.xml



<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>pl.matt</groupId>
<artifactId>aquarium</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>aquarium Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
</dependency>
</dependencies>
<build>
<finalName>aquarium</finalName>
<directory>/tmp/target</directory>
<outputDirectory>/tmp/target/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>




Dodałem w sekcji dependencies zależności do springa, springa-webmvx, servlet API i freemarkera.
SpringMVC w warstwie prezentacji może korzystać z wielu technologii, m. in. JSP, szablonów Velocity i Freemarkera. Wybrałem tą ostatnią, bo jeszcze z niej nie korzystałem - może będzie ciekawiej.
Poczyniłem jeszcze pewne zmiany w sekcji build. Tagi directory i outputDirectory pozwalają wskazać katalog, gdzie maven będzie budował aplikację. Ustawiam je na katalog tmp, nie lubię jak mi się coś buduje w przestrzeni roboczej (workspace) środowiska Eclipse. Wtyczka (plugin) maven-compiler-plugin pozwala ustawić poziom kompilacji na Javę w wersji 1.6 (domyślnie jest 1.4).


Jeszcze trochę konfiguracji. Springa można podłączyć do aplikacji webowej na 2 sposoby. Za pomocą serwletu, przez który będą przechodziły żądania HTTP lub za pomocą filtra. Wybieram to pierwsze podejście.
Edytuję plik 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>springapp</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

</web-app>


Dodałem servlet springapp, przez który przejdą wszystkie żądania http do zasobów o rozszerzeniu *.htm. Nimi zajmie się spring. Konfiguracja springa znajdzie się w pliku springapp-servlet.xml (ponieważ nasz serwlet nazwałem springapp). Plik ten trzeba dodać do katalogu WEB-INF



<?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">

<!-- the application context definition for the springapp DispatcherServlet -->

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" />

<!-- freemarker config -->
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath"
value="/freemarker/" />
</bean>

<!--

View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.

-->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />

<!-- if you want to use the Spring FreeMarker macros, set this property to true -->
<property name="exposeSpringMacroHelpers" value="true" />

</bean>

</beans>



Co się tutaj dzieje? W pliku springapp-servlet.xml mamy 2 rzeczy.

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" />
mówi Springowi, że rządania zasobu info.htm będzie realizowała klasa pl.matt.aquarium.InfoController.

Reszta to konfiguracja FreeMarkera. Raczej wiadomo o co chodzi.
Teraz czas stworzyć klasę zarządcy (controller we wzorcu MVC).



package pl.matt.aquarium;

import java.io.IOException;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
* @author mateusz
*
*/
public class InfoController implements Controller {

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

int fishCount = (int) (Math.random() * 100);
return new ModelAndView("info", "fishCount", fishCount);
}

}




Każdy zarządca ww SpringMVC implementuje
org.springframework.web.servlet.mvc.Controller. Mój, InfoController, sprawdza (losuje) ile mam rybek w akwarium. Wylosowaną wartość przekazuje do warstwy prezentacji, o nazwie "info".
Stworzę więc i ją - plik info.ftl w katalogu webapp/freemarker

<html>
<body>
<h2>Masz ${fishCount} rybek</h2>
</body>
</html>

Proste, prawda? Żadnych taglibów, ani innych bajerów. Praktycznie czysty HTML. Za fishCount zostanie oczywiście podstawiona wartość wylosowana w klasie InfoController.

Czas to wszystko uruchomić. Ponownie daję maven install. Jeżeli nie podlinkowałeś wara, musisz go ponownie skopiować do katalogu webapps Tomcata.
i wchodzę na adres:
http://localhost:8080/aquarium/info.htm

na koniec konkurs. Ile macie rybek?
Ja mam 25.

niedziela, 1 czerwca 2008

instaluję(my) mavena 2

Wyobraźcie sobie, że od dłuższego czasu byłem dużym przeciwnikiem Mavena. Dlaczego?
Kiedyś mój mentor Javowy powiedział, że nie wierzy, że ludzie, którzy zrobili Mavena 1 (który podobno okazał się niewypałem), nie mogą zrobić porządnego. Mnie to wystarczyło, więc wszyscy guru-mentorzy-blogerzy - uważajcie co mówicie. Choć trzeba przyznać, że coś jest na rzeczy. Z prezentacji Piotra Tabora "Okiełznać Mavena 2" (WJUG) wyniosłem taki wniosek, że to wszystko jest tam tak zakręcone, że nikt nie wie, o co chodzi...

Nie wiem, co to jest Maven 1. Co to jest Maven 2?
Maven 2 to 3 rzeczy (jeszcze się nie pogubiłem w numerkach)
- to taki Ant o trochę bogatszych możliwościach (generalnie rzecz ujmując buduje - kompiluje projekt)
- to takie Ivy. Nie wiecie, co to jest Ivy? Mój mentor wiedział ;). Ja teraz też wiem. To po prosty zarządca zależności - ściąga biblioteki w wersjach, w których mu każemy.
- to takie coś, dla czego nie znam alternatywy i dlaczego zaraz zainstaluję mavena. Generalnie to narzędzie umożliwia postawienie projektu, za pomocą tzw. archetypów. Pobiera gotowe struktury katalogów i robi nam aplikację typu "hello word" w jednej z wielu wybranych przez nas technologii. Może ty, szanowny czytelniku znasz tutaj jakąś alternatywę? Ja dla tych archetypów właśnie instaluję maven2, więc twoja informacja się nie zmarnuje.

Ale do rzeczy.

Kiedyś było trudno zainstalować mavena2. Tworzyło się jakieś katalogi, dodawało jakieś zmienne środowiskowe, sranie w banie. Teraz mamy ubuntu. Ja mam Ubuntu 8.04.
Tak więc aby na Ubuntu zainstalować maven2, odpalamy konsole i wpisujemy

sudo apt-get install maven2

komputer warczy, warczy...
i jest gites.
Sprawdzam, czy działa:
mateusz@mateusz-laptop:~$ mvn [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [ERROR] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] You must specify at least one goal. Try 'mvn install' to build or 'mvn -?' for options See http://maven.apache.org for more information. [INFO] ------------------------------------------------------------------------ [INFO] For more information, run Maven with the -e switch [INFO] ------------------------------------------------------------------------ [INFO] Total time: <> [INFO] Finished at: Mon Jun 02 00:59:59 CEST 2008 [INFO] Final Memory: 1M/34M [INFO] ------------------------------------------------------------------------

Wyskoczył error, czyli działa. Takie rzeczy tylko na moim blogu ;)

Jedziemy dalej.
rzut oka na:
Maven in 5 Minutes
Maven Getting Started Guide
oczywiście nie ma tego czego szukam

dobra, wpisuję:

mvn archetype:generate -DarchetypeCatalog=http://myfaces.apache.org

coś się dzieje...
Ściągają się jakieś jary. Już wiem o co chodzi. Podałem
-DarchetypeCatalog=http://myfaces.apache.org

Co oznacza, że maven2 wszedł na stronę

http://myfaces.apache.org

i zobaczył, jakie tam są archetypy ;). Komputer robi coś za mnie. Fajnie.
Coś czuję, że zaraz będziemy mieli pierwszą aplikację z mavena.

[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0) Choose archetype:
1: remote -> myfaces-archetype-helloworld (Simple Web application using Apache Myfaces)
2: remote -> myfaces-archetype-helloworld-facelets (Simple Web application using Apache Myfaces and Facelets)
3: remote -> myfaces-archetype-helloworld-portlets (Simple Web application using Apache Myfaces and Portlets)
4: remote -> myfaces-archetype-jsfcomponents (Simple JSF Component using Apache Myfaces)
5: remote -> myfaces-archetype-trinidad (Simple Web application using Apache Myfaces and Trinidad)

nooo, jesteśmy w domu.
Trynidad i Tobago – wyspiarskie państwo Ameryki Środkowej, położone u północnych brzegów Ameryki Południowej.
Dla mnie bomba. Wybieramy 5 i lecimy dalej.

Downloading: http://repo1.maven.org/maven2/org/apache/myfaces/buildtools/myfaces-archetype-trinidad/1.0.1/myfaces-archetype-trinidad-1.0.1.jar
21K downloaded
Define value for groupId: : pl.najawie
Define value for artifactId: : trynidad-app
Define value for version: 1.0-SNAPSHOT: : 1.0.0
Define value for package: : pl.najawie
Confirm properties configuration:
groupId: pl.najawie
artifactId: trynidad-app
version: 1.0.0
package: pl.najawie
Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: myfaces-archetype-trinidad:1.0.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: pl.najawie
[INFO] Parameter: packageName, Value: pl.najawie
[INFO] Parameter: package, Value: pl.najawie
[INFO] Parameter: artifactId, Value: trynidad-app
[INFO] Parameter: basedir, Value: /home/mateusz
[INFO] Parameter: version, Value: 1.0.0
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: /home/mateusz/trynidad-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8 minutes 50 seconds
[INFO] Finished at: Mon Jun 02 01:25:07 CEST 2008
[INFO] Final Memory: 9M/57M
[INFO] ------------------------------------------------------------------------


tym razem sukces, czyli znowu sukces. jeden minus, coś tam mi powstało w katalogu /home/mateusz/trynidad-app
Będę to musiał jakoś skonfigurować. Ale na razie się tym nie przejmujmy.
Teraz:
cd trynidad-app/
mvn package

coś się ściąga... Poczytam o Trynidadzie i Tobago. No tak, przecież Beenhakker ich trenował poprzednio. Dobra, idziemy dalej.

mvn jetty:run


Ściąga się Jetty. Mój ulubiony kontener serwletów.
Wyskoczyło:

2008-06-02 01:34:41.398::INFO: Started SelectChannelConnector@0.0.0.0:8080 [INFO] Started Jetty Server [INFO] Starting scanner at interval of 10 seconds.

Z czego wnioskuję, że coś znajdę na
http://localhost:8080




klikam w link, wybieram index.jspx i...

java.lang.RuntimeException: FacesContext not found
O co chodzi? No dobra, okazało się, że ten Trynidad wybrałem w sposób nie do końca losowy i wiem mniej więcej o co chodzi. Generalnie ten trynidad który wybrałem, to taki dodatek do MyFaces która jest implementacją Java Servlet Faces.
W wyjątku tym chodzi o to, że link jest wpisany źle i plik jspx jest odpalany bez JSFów. Krótka analiza pliku web.xml i już wiem, że muszę wpisać:

http://localhost:8080/trynidad-app/faces/index.jspx

i jesteśmy w domu.



No, to tyle na teraz, ale w głowie mam już pomysły na kilka kolejnych wpisów, więc bądźcie czujni...

będzie blog

no dobra, oszukałem was.

Tylko krowa nie zmienia poglądów. Będzie jednak blog. Dlaczego?
Wbrew temu, co inni rozgłaszają, nie dla tego, żebym sobie wpisał w CV.
A dla tego...
Ostatnimi dniami biorę się ostro za magisterkę. Szukam materiałów, to na gazeta.pl, to na onecie...
Wpadłem na artykuł:
Szał zakupów z zielonymi w kieszeni

gdzie jest napisane
"Wyjątkowo tanie w USA są ubrania, np. dżinsy Bossa, które kosztują w Polsce ponad 400 zł, w amerykańskim centrum handlowym dostać można za połowę tego"

a ja sobie czytam:
"Wyjątkowo tanie w USA są ubrania, np. dżinsy JBossa, które..."

ojojoj... Coś ze mną nie tak?
Nie, to mnie po prostu kręci. Nie Boss, ale właśnie JBoss, Java, Ubuntu i radiowa trójka..

Nadzieja, że ktoś moje wypociny przeczyta, trochę się uśmiechnie, trochę mu się to przyda napełnia mnie satysfakcją. Co więcej, odkryłem dziś 1 komentarz na moim blogu i 1 na mojej wiki.

Ten do wiki - bezcenny. Mój sposób jakoby nie działał. Może ktoś potwierdzić że działa / nie działa moja strona błędu w JSF?

Byłbym wdzięczny.

Dziś mam wenę na pisanie, więc trzymajcie się. To nie jest ostatni post.