piątek, 1 sierpnia 2008

zdalne debugowanie aplikacji na serwerze aplikacyjnym JBoss

Istnieją jeszcze starej daty programiści którzy lubią pracę z konsolą. Nie uruchamiają oni serwerów aplikacyjnych z poziomu Eclipse, korzystają za to z terminala. Zaliczam się do nich i ja. Podejście takie ma jedną zasadniczą wadę. W Eclipse, aby uruchomić serwer aplikacyjny w trybie debug wystarczy na zakładce Servers kliknąć odpowiednią ikonkę. Z poziomu konsoli jest nieco trudniej. Ale tylko troszeczkę...

Ręczne uruchamianie serwera w trybie debug może się także przydać przy debugowaniu serwera uruchomionego na innym komputerze, niż komputer programisty. Instrukcję jak to zrobić, na przykładzie Eclipse 3.4.0 i serwera JBoss 4.0.5.GA przedstawiam poniżej.

W przykładzie, korzystam z aplikacji akwarium, którą od czasu do czasu posiłkuję się na blogu.

Najpierw zajmę się JBossem. W katalogu bin znajduje się plik run.sh. Kopiuję go na debug.sh. Na początku pliku dodaję:

JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"

To polecenie modyfikuje zmienną JAVA_OPTS, której zawartość dalej w skrypcie staje się parametrami uruchomieniowymi wirtualnej maszyny (JVM) z którą wystartuje serwer. Dobrze jest zapamiętać wartość wpisaną jako address i ustawić suspend=n. W przeciwnym wypadku JBoss nie uruchomi się dopóki nie podłączymy się do niego z IDE.

W JBossie to już wszystko. Czas na Eclipse.

W klasie AquariumManagerImpl ustawiam pułapkę, klikając na lewym marginesie.



Tutaj zatrzyma się aplikacja aby umożliwić śledzenie jej wykonania krok po kroku.

Następnie wybieram z menu Debug pozycję Debug Configurations....




Wybieram z listy po lewej stronie Remote Java Application podając odpowiedni port i nazwę.



Klikam Apply i Debug. Wyskoczy błąd, ponieważ serwer nie jest jeszcze włączony.

Czas zatem go uruchomić, tym razem nie poleceniem
./run.sh
a
./debug.sh

JBoss powienien wypluć z siebie:
Listening for transport dt_socket at address: 8787

Następnie, w Eclipse z menu Debug wybieram pozycję aquarium - debug.

Wchodzę na stronę:
http://localhost:8080/aquarium/info.htm.
Strona się nie ładuje, przechodzę do Eclipse. Zatwierdzam zmianę perspektywy i już mogę przechodzić kod aplikacji linijka po linijce za pomocą klawisza F6
F8 natomiast wznawia dalszy bieg programu.

wtorek, 29 lipca 2008

Sakiewka na Ubuntu 8.04

Witam ponownie. Dawno nic nie pisałem, a to wszystko przez moje przygody z wine. Otóż udało mi się odpalić za jego pomocą Civilization IV wraz z dodatkami. Świetna muzyka i świetna zabawa na długie miesiące...

Jak się okazuje, wstęp tego posta nie jest całkiem od parady... albowiem dziś też poeksperymentuję trochę z wine. Poniżej przedstawię sposób, na zainstalowanie programu Sakiewka na Ubuntu 8.04. Sakiewka, to program Jerzego Olszewskiego wspomagający inwestowanie na giełdach papierów wartościowych. Program napisany jest pod Windows, więc bez wine się nie obejdzie;

~$ sudo apt-get install wine
Czytanie list pakietów... Gotowe
Budowanie drzewa zależności
Odczyt informacji o stanie... Gotowe
wine jest już w najnowszej wersji.
0 aktualizowanych, 0 nowo instalowanych, 0 usuwanych i 0 nieaktualizowanych.

ja już mam wine zainstalowane...

Czas pobrać wersję instalacyjną Sakiewki.

rozpakować:

$ unzip DISK1.zip
Archive: DISK1.zip
creating: DISK1/
inflating: DISK1/setup.exe
inflating: DISK1/bdemerge.ini

i odpalić z wine
wine DISK1/setup.exe

Powita nas instalator:



klikam kilka razy dalej i wskazuję katalog instalacyjny



Obyło się bez przeszkód. Czas na odpalenie programu.

~$ cd /opt/sakiewka/Sakwa
:/opt/sakiewka/Sakwa$ wine Sakiewka.exe

No proszę, wszystko działa.



W sumie to dobrze, bo najwyższy czas spać...

czwartek, 3 lipca 2008

oferty-pracy-java grupa dla szukających pracy lub pracowników

Witam.

Kilka dni temu powstała dedykowana dla jawowców i ludzi którzy ich poszukują grupa:
oferty-pracy-java.

Grupa w swym założeniu ma łączyć osoby poszukujące pracy związanej z Javą z osobami poszukującymi pracowników. Można tam zamieścić ogłoszenie lub własne CV. O zamieszczonych CV i ogłoszeniach można również na grupie dyskutować.
Grupa jest moderowana i publicznie dostępna, ale tylko osoby do niej zapisane mogą zamieszczać ogłoszenia oraz inne wiadomości. Zarejestrowani użytkownicy mogą również otrzymywać powiadomienia pocztą elektroniczną o nowych ogłoszeniach.

Zastanawiacie się po czym się poznaje mężczyznę? Mężczyznę poznaje się podobno po tym, jak kończy, więc na koniec dodam, że grupa zawiera tylko i wyłącznie ogłoszenia z podanym wynagrodzeniem. Nawiasem pisząc ja poznaję mężczyzn zupełnie inaczej.

Zapraszam więc zarówno do zapisania się do grupy, jak i rozpropagowaniu informacji o niej (również w działach i firmach HR), co niniejszym czynię.

Pozdro666!

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.