niedziela, 14 czerwiec 2009

Filmy się obrobiły

Stało się.

Wszystkie filmy z warszawskiego Eclipse Demo Campu pojawiły się na stronie:
http://javatv.pl/.

Muszę przyznać, że jakoś jest niezła, a i 15 minutowe nagrania ogląda się przyjemniej niż 2 godzinne.

Oto moje wystąpienie:

poniedziałek, 8 czerwiec 2009

Garść wrażeń po warszawskim Eclipse DemoCamp Galileo 2009

W ubiegły wtorek, 2giego czerwca w Warszawie w budynku MIMUW przy ul. Banacha 2 odbyły się warsztaty poświęcone najnowszemu wydaniu Eclipse IDE (jeszcze w wersji RC) Eclipse DemoCamp Galileo 2009.

Warsztaty poprzedził poczęstunek z pizzy, co okazało się sprawką patrona Code-House.org.

Formuła imprezy była dość nietypowa, przynajmniej jak na warszawskie spotkania. Warsztaty składały się z 6ciu 15 minutowych wystąpień. Jednym z nich, było wystąpienie "Wtyczka do Eclipse w 5 ... no może 15 minut" autorstwa... mojego autorstwa.

Zaczęło się całkiem nietypowo, bo od kłopotów technicznych. Projektor odmówił współpracy z moim Ubuntu w rozdzielczości wyższej niż 800x600. A że prezentacja trwała 15 minut... to i skończyło się nietypowo, ponieważ tworząc wtyczkę nie mogłem zaznaczyć odpowiednich opcji konfiguracyjnych. Nie mieściły się one po prostu na ekranie. I tak zamiast odtwarzacza mp3jek Eclipse zamienił się tylko w prymitywny manager plików.

Jeżeli ktoś miałby ochotę zobaczyć co chciałem osiągnąć, a nie tylko to co mi się udało "programując przez dziurkę od klucza" jak to określił słuchacz w pierwszym rzędzie, kod wtyczki odtwarzającej pliki MP3 zamieszczam tutaj.

Niedługo możecie spodziewać się krótkiego poradnika, a ja nie mogę doczekać się nagrania z konferencji.

piątek, 17 kwiecień 2009

Pudelek Eclipse Plug-in

Zastanawiałeś się jak zwiększyć wydajność pracy Java Teamu w Twojej firmie? Wiesz ile czasu tracą programiści przełączając się pomiędzy Eclipsem a przeglądarką internetową, żeby przejrzeć najnowsze wiadomości?
To na szczęście już przeszłość.

Dzięki wtyczce Pudelek Eclipse Plug-in zintegrujesz swoje ulubione środowisko deweloperskie, z najpopularniejszym serwisem plotkarskim. Dzięki intuicyjnemu interfejsowi użytkownika (artykuły widziane są jako pliki .java), od razu połapiesz się co i jak.



I to wszystko za darmo.
Zapraszam do instalacji, używania, komentowania i rozpowszechniania wtyczki.
Strona domowa projektu:
http://sites.google.com/site/pudelekeclipseplugin/

środa, 15 kwiecień 2009

Jazoon09 rozkład jazdy

Całkiem niedawno pojawił się plan konferencji Jazoon09 (czerwiec 2009).
I tak w
poniedziałek dominuje GlassFish i OSGi,
we wtorek kupę smakołyków. Między innymi Spring 3, Glassfish 3 i JBoss AS 5.0
w środę między innymi Jazoon Rookie, Java FX
we czwartek oprócz lunchu dopieszczanie GC i co nieco o JPA 2.0.
a w piątek rządzić będzie bezpieczeństwo.
Zapowiada się bardzo interesująco, tylko gdzie impreza integracyjna?

niedziela, 12 kwiecień 2009

leniwe ładowanie właściwości w Hibernate

Święta, święta i... jeszcze został jeden dzień obżarstwa. Tu barszczyk, tam kiełbaska, mięsko, sałatka, mazurek. Wszystkiego trzeba spróbować. A gdyby tak jeść tylko to, co jest w danej chwili niezbędne? Po prostu nie przejadać się. Chyba byłoby zdrowiej...

To takie moje świąteczne przemyślenia. W programowaniu jednak jest trochę jak w życiu. Często mamy do czynienia z podobnymi sytuacjami. Nie zjadamy co prawda w naszych programach wszystkiego, co jest pod ręką, ale wyciągając z bazy danych obiekty przy użyciu narzędzi ORM (Object Relational Mapping), pobieramy je wyciągając wszystkie kolumny z bazy danych. Inicjujemy wszystkie, nawet te niepotrzebne w danej chwili właściwości obiektów.

O ile zazwyczaj nikomu to nie przeszkadza, o tyle w przypadku pobierania właściwości o dużych rozmiarach (np. duże obiekty binarne, tekstowe) może to się odbić czkawką i negatywnie wpłynąć na wydajność aplikacji.

Na szczęście Hibernate, będący chyba najpopularniejszym narzędziem mapowania obiektowo relacyjnego, umożliwia selektywne pobieranie właściwości obiektów. Wyboru, które kolumny pobieramy wyciągając obiekt z relacyjnej bazy danych dokonujemy za pomocą parametru fetch adnotacji @Basic. Jeżeli konfigurację przejścia obiektowo relacyjnego przechowujemy w plikach XML, za to zachowanie odpowiedzialny jest atrybut lazy znacznika property.

Domyślnie pobierane są wszystkie atrybuty obiektów, nie będące kolekcjami, co odpowiada ustawieniom:

@Basic(fetch = FetchType.EAGER)
oraz

<property lazy="false" />

Sprawdźmy więc w akcji, jak działa konfiguracja leniwego pobierania wybranych właściwości obiektów w Hibernate. Mój przykładowy program powstał na podstawie przykładów ze strony projektu Hibernate.

Pobierał będę encję Message

package pl.matt.model;

public class Message {
private Long id;
private String text;

Message() {}

public Message(String text) {
this.text = text;
}

public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}

public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}


konfigurację przejścia obiektowo relacyjnego zawarłem w pliku XML Message.hbm.xml


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class
name="pl.matt.model.Message"
table="MESSAGES">

<id
name="id"
column="MESSAGE_ID"
>
<generator class="native"/>
</id>

<property
name="text"
column="MESSAGE_TEXT"
lazy="false"
/>

</class>

</hibernate-mapping>


Na prosty program pobierający obiekt z bazy danych składa klasa Main

package pl.matt.main;

import org.hibernate.Session;
import org.hibernate.Transaction;

import pl.matt.model.Message;
import pl.matt.utils.HibernateUtil;

public class Main {

public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Message message = (Message) session.get(Message.class, 1l);
System.out.println(message.getId());

tx.commit();
session.close();

HibernateUtil.shutdown();
}
}


Klasa narzędziowa HibernateUtil zawiera kilka metod ułatwiających pracę z Hibernate:

package pl.matt.utils;

import org.hibernate.*;
import org.hibernate.cfg.*;

/**
* Startup Hibernate and provide access to the singleton SessionFactory
*/
public class HibernateUtil {

private static SessionFactory sessionFactory;

static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
// Alternatively, we could look up in JNDI here
return sessionFactory;
}

public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}


W pliku hibernate.cfg.xml konfiguruję połączenie z bazą danych HSQL oraz ustawiam wartość true parametru show_sql który umożliwia podglądanie zapytań SQL wykonywanych przez Hibernate.


<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>

<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="hibernate.connection.username">sa</property>

<!-- SQL to stdout logging -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>

<property name="hibernate.hbm2ddl.auto">update</property>

<property name="dialect">org.hibernate.dialect.HSQLDialect</property>

<mapping resource="pl/matt/model/Message.hbm.xml"/>

</session-factory>
</hibernate-configuration>


Uruchamiam program i zgodnie z oczekiwaniami widzę zapytanie pobierające 2 właściwości z tabeli MESSAGES:

Hibernate:
/* load pl.matt.model.Message */ select
message0_.MESSAGE_ID as MESSAGE1_0_0_,
message0_.MESSAGE_TEXT as MESSAGE2_0_0_
from
MESSAGES message0_
where
message0_.MESSAGE_ID=?


zmieniam w pliku Message.hbm.xml wartość atrybutu lazy na true. Uruchamiam ponownie program i tym razem niezgodnie z oczekiwaniami widzę ponownie:

Hibernate:
/* load pl.matt.model.Message */ select
message0_.MESSAGE_ID as MESSAGE1_0_0_,
message0_.MESSAGE_TEXT as MESSAGE2_0_0_
from
MESSAGES message0_
where
message0_.MESSAGE_ID=?


Hmm... słabo.
Zagłębiłem się w dokumentację i już widzę, że nie będzie łatwo:

Note

To enable property level lazy fetching, your classes have to be instrumented: bytecode is added to the original one to enable such feature, please refer to the Hibernate reference documentation. If your classes are not instrumented, property level lazy loading is silently ignored.

Nawet nie wiedziałem, że jest taki czasownik instrument. W każdym razie moje ustawienia zostały po cichu zignorowane, jak to ładnie określiła dokumentacja Hibernate. Trzeba by to zmienić. Szperam więc dalej w poszukiwaniu sposobu zinstrumetowania klas.

Wykonać ten proces można antowym zadaniem:

<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
<classpath path="${build.dir}"/>
<classpath refid="project.classpath"/>
</taskdef>

<instrument verbose="true">
<fileset dir="${build.dir}/pl/matt/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>


W dokumentacji Hibernate jest mały błąd, gdyż klasa zadania instrument InstrumentTask nie znajduje się w pakiecie org.hibernate.tool.instrument tylko w org.hibernate.tool.instrument.cglib.

Jako, że powyższe zadanie zmienia kod plików *.class, muszę uruchamiać te skompilowane i zmienione przez ANTa pliki. Posłużę się do tego celu poniższym plikiem build.xml:

<project name="hibernateLazyBasic" default="compile" basedir=".">

<!-- Name of project and version -->
<property name="proj.name" value="hibernateLazyBasic"/>
<property name="proj.shortname" value="hibernateLazyBasic"/>
<property name="version" value="1.0"/>

<!-- Global properties for this build -->
<property name="database.dir" value="database"/>
<property name="src.java.dir" value="src/java"/>
<property name="lib.dir" value="lib"/>
<property name="build.dir" value="build"/>

<!-- Classpath declaration -->
<path id="project.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
<include name="**/*.zip"/>
</fileset>
</path>

<!-- Useful shortcuts -->
<patternset id="meta.files">
<include name="**/*.xml"/>
<include name="**/*.properties"/>
</patternset>

<!-- Clean up -->
<target name="clean" description="Clean the build directory">
<delete dir="${build.dir}"/>
<mkdir dir="${build.dir}"/>
</target>

<!-- Compile Java source -->
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.java.dir}"
destdir="${build.dir}"
classpathref="project.classpath"/>
</target>

<!-- Copy metadata to build classpath -->
<target name="copymetafiles">
<mkdir dir="${build.dir}"/>
<copy todir="${build.dir}">
<fileset dir="${src.java.dir}">
<patternset refid="meta.files"/>
</fileset>
</copy>
</target>

<target name="run-instrumented" depends="clean, instrument, copymetafiles">
<java fork="true"
classname="pl.matt.main.Main"
classpathref="project.classpath">
<classpath path="${build.dir}"/>
</java>
</target>

<target name="run" depends="clean, compile, copymetafiles">
<java fork="true"
classname="pl.matt.main.Main"
classpathref="project.classpath">
<classpath path="${build.dir}"/>
</java>
</target>

<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
<classpath path="${build.dir}"/>
<classpath refid="project.classpath"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${build.dir}/pl/matt/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>

</project>


zadanie o nazwie run-instrumented kompiluje, przeprowadza instrumentację klas z pakiedu pl.matt.model i uruchamia program.

Po jego wykonaniu, otrzymuję na konsoli:

[java] Hibernate:
[java] /* load pl.matt.model.Message */ select
[java] message0_.MESSAGE_ID as MESSAGE1_0_0_
[java] from
[java] MESSAGES message0_
[java] where
[java] message0_.MESSAGE_ID=?


Ładowane jest tylko pole ID z tabeli MESSAGES, czyli zgodnie z oczekiwaniami. Kolumna MESSAGE_TEXT pobierana jest z bazy osobnym zapytaniem:

[java] Hibernate:
[java] /* sequential select
[java] pl.matt.model.Message */ select
[java] message_.MESSAGE_TEXT as MESSAGE2_0_
[java] from
[java] MESSAGES message_
[java] where
[java] message_.MESSAGE_ID=?

dopiero wtedy, kiedy będzie potrzebna.

Generalnie wszystko działa, tylko trzeba odrobinę zmienić proces kompilacji programu.
Kod źródłowy przedstawiający powyższe rozwiązanie znajdziesz tutaj

a że święta już w połowie, pozostaje mi życzyć Wam mokrego dyngusa.

wtorek, 31 marzec 2009

Jazoon Cutting Edge

Jeżeli zawsze chciałeś opowiedzieć coś ciekawego programistom Java, świetnie trafiłeś. Masz pomysł na prezentację, ale boisz się, że nie będziesz w stanie zająć 60, czy 90 minut?

Jazoon Cutting Edge - These 20 minutes talks address the most recent developments in the Java industry.
Call for papers: 15 April 2009
Submission deadline: 15 May 2009

i wszystko wiadomo.

Do zgarnięcia darmowa wejściówka czerwcowy Jazoon i 50% zniżki dla osoby towarzyszącej (również w prezentacji).
Miło byłoby usłyszeć kogoś z Polski.

Może Jacek się zgłosi, ze swoimi zabawkami? Kto nie był dziś na spotkaniu warszawskiego JUGa niech żałuje.

niedziela, 22 marzec 2009

Ziarna sesyjne - czar prysł.

Dopiero co zachwycałem się nad lokalnym i zdalnym sposobem wywoływania usług ziaren EJB. Przyszła wiosna, śnieg stopniał i czar prysł. Otóż pomiędzy wywołaniem danej metody poprzez interfejs oznaczony adnotacją @Local a wywołaniem za pomocą interfejsu @Remote mogą być subtelne różnice. Bierze się to stąd, że parametry metod wywoływanych lokalnie (@Local) przekazywane są poprzez referencje, parametry metod zdalnych (@Remote) przez wartość.

Zobaczmy o co chodzi na podstawie prostego przykładu. Stworzę z tej okazji w NetBeans IDE 6.5.1 projekt ziarna EJB oraz projekt klienta EJB.

Zaczynam od aplikacji EJB:
Z menu File wybieram pozycję New Project:


następnie wybieram kategorię Java EE i projekt EJB Module
klikam Next, po czym na zakładce Server and Settings dodaję nową konfigurację serwera, klikając przycisk Add...

Przykład będę uruchamiał na serwerze JBoss 4.2.2.GA


więc wskazuję jego położenie na dysku:


klikam Next> i Finish. Projekt gotowy.

W projekcie stworzę proste ziarno, które będzie udostępniało metodę dodającą podany jako parametr element do podanej jako parametr kolekcji:


public Collection add(Collection collection, Object element) {
collection.add(element);
return collection;
}


Ziarno będzie implementowało interfejs lokalny oraz zdalny. Ziarno oraz interfejsy przedstawiają się następująco:

Kod ziarna StatelessBean:

package pl.matt.session;

import java.util.Collection;
import javax.ejb.Local;
import javax.ejb.Remote;


@javax.ejb.Stateless
@Remote(StatelessRemote.class)
@Local(StatelessLocal.class)
public class StatelessBean implements StatelessRemote, StatelessLocal {

public Collection add(Collection collection, Object element) {
collection.add(element);
return collection;
}

}


interfejs zdalny StatelessRemote:

package pl.matt.session;

import java.util.Collection;
import javax.ejb.Remote;

public interface StatelessRemote {

Collection add(Collection collection, Object element);

}


i identyczny interfejs lokalny StatelessLocal:

package pl.matt.session;

import java.util.Collection;
import javax.ejb.Remote;

public interface StatelessLocal {

Collection add(Collection collection, Object element);

}


Całość uzupełnia ziarno testujące dodawanie elementów do kolekcji, które próbuje zarówno zdalnie jak i lokalnie dodać 3 napisy do listy:

interfejs TestRemote:

package pl.matt.session;

public interface TestRemote {
void test();
}


oraz jego implementacja:


package pl.matt.session;

import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remote;
import javax.naming.Context;
import javax.naming.InitialContext;

@javax.ejb.Stateless
@Remote(TestRemote.class)
public class TestBean implements TestRemote {

public void test() {
try {
Context context = new InitialContext();
StatelessLocal statelessLocal = (StatelessLocal) context.lookup("StatelessBean/local");
StatelessRemote statelessRemote = (StatelessRemote) context.lookup("StatelessBean/remote");
List<String> localList = new ArrayList();
statelessLocal.add(localList, "1");
statelessLocal.add(localList, "2");
statelessLocal.add(localList, "3");
System.out.println("local list: " + localList);

List<String> remoteList = new ArrayList();
statelessRemote.add(remoteList, "1");
statelessRemote.add(remoteList, "2");
statelessRemote.add(remoteList, "3");
System.out.println("remote list: " + remoteList);
} catch (Exception e) {
e.printStackTrace();
}
}
}


Do wywołania metody ziarna TestBean potrzebuję klienta EJB. Tworzę go wybierając ponownie pozycję New Project... z menu File. Następnie z kategorii Java EE wybieram projekt Enterprise Application Client:

Do tak stworzonego projektu dodaję jako zależność stworzony uprzednio projekt EJB


Dzięki temu, będę w nim mógł uzyskać dostęp do interfejsu testującego. Całość kodu klienta zawarta jest w metodzie main():


package pl.matt;

import javax.naming.Context;
import javax.naming.InitialContext;
import pl.matt.session.TestRemote;

public class Main {

public static void main(String[] args) {
try {
System.out.println("start");
Context context = new InitialContext();
TestRemote test = (TestRemote) context.lookup("TestBean/remote");
test.test();
System.out.println("finished");
} catch (Exception e) {
e.printStackTrace();
}

}
}


Pozostało uruchomienie i przekonanie się, jak to wszystko będzie działać. Klikam prawym przyciskiem myszy na projekcie EJB, wybieram opcję Run. Następnie to samo robię na projekcie klienta EJB. JBoss się uruchamia, po chwili widzę w konsoli:


22:27:55,969 INFO [STDOUT] local list: [1, 2, 3]
22:27:55,974 INFO [STDOUT] remote list: []


2 razy wywołałem tę samą metodę, raz przez interfejs @Remote, raz przez @Local i wynik jej działania jest inny. Do kolekcji, przekazanej przez wartość elementy nie zostały dodane (tzn. zostały, ale do kopii tej kolekcji, przekazanej do ziarna EJB).

Trzeba uważać.