11 listopada 2006

Pierwsza aplikacja z JSF 1.2 i EJB 3.0

Wiadomy już jest termin, kiedy w końcu odetchnę i pełną piersią będę oddychał Java EE 5 ;-) Właśnie wysłałem wiadomość na Warszawa-JUG o kolejnym spotkaniu, które zgodnie z założeniami powinno odbyć się w pierszy wtorek grudnia, czyli 5.12.2006 o godzinie 18:00. Chciałbym na nim przedstawić Java EE i zaprezentować tworzenie aplikacji z jej użyciem (być może uda mi się to wpełni zautomatyzować z użyciem Maven 2). Zakasałem rękawy i przygotowuję się pełną parą...

Po instalacji Glassfish (od tej pory zwanego GF) oraz NetBeans IDE (NB) postanowiłem skorzystać z możliwości ich integracji i stworzyć pierwszą aplikację Java EE z ich użyciem. Jakież było moje zdumienie, kiedy po niecałych 5 minutach zobaczyłem działającą aplikację opartą o JSF 1.2 i EJB 3.0, a dokładnie JPA (!) To było dokładnie to czego potrzebowałem - kilka ruchów myszką i uderzeń w klawiaturę i mam aplikację z pełnymi źródłami do analizy. Zrozumienie aplikacji nie zabiera więcej niż kolejne 10-15 minut (oczywiście zakładając znajomość takich pojęć jak JSF w ogólności, kontenery IoC i Java Annotations). Z niecierpliwością oczekuję projektu, w którym przyjdzie mi stosować cudeńka Java EE i SE 5. Tworzenie aplikacji Java EE nie mogło być prostsze!

Opis przykładowej aplikacji po angielsku to:

Demonstrates the use of Java Persistence APIs based on the new Java Persistence specification in conjunction with JavaServer Faces that creates a simple authentication scheme using JavaServer Faces PhaseListeners and a simple Entity bean representing a user.

Dla zainteresowanych samodzielnym przejściem mojej "ścieżki zdrowia" przedstawiam kolejne kroki. Założeniem jest, aby GF i NB były zainstalowane.
  1. Uruchamiamy NB
  2. Definiujemy GF w menu Servers w zakładce Runtime w NB.
  3. Otwarcie NB wiąże się z jednoczesnym uruchomieniem okna Welcome z różnymi pozycjami związanymi z NB, gdzie w dolnym lewym rogu znajduje się Sample Projects, a tam pozycja Enterprise.
  4. Wybranie Enterprise to uruchomienie pomocnika (ang. wizard) , gdzie wybieramy JsfJpa (prawy panel - Projects) i wciskamy przycisk Next.
  5. Podajemy położenie projektu i wciskamy przycisk Finish.
  6. Tak utworzyliśmy nasz projekt JsfJpa (zakładka Projects) z wykorzystaniem JSF i JPA.
  7. Zaznaczamy projekt JsfJpa i z menu pod prawym klawiszem myszy wybieramy menu Run Project.
  8. Uruchamia się automatycznie Java DB oraz GF i przeglądarka z adresem http://localhost:8080/jsf-jpa-war/. To kończy naszą dzisiejszą pracę.
Warto zapoznać się ze źródłami aplikacji i jednocześnie prześledzić pracę aplikacji w konsoli GF (zakładka u dołu NB o nazwie jaką nadaliśmy GF definiując go w Runtime). Proste? Niezwykle! Właśnie takie powinno być programowanie/tworzenie aplikacji Java EE w XXI wieku! ;-)

5 komentarzy:

  1. Witam.

    Jak wystartować projekt w Maven korzystający z EJB3 oraz JSF?

    OdpowiedzUsuń
  2. EJB3 - to nic innego jak pom=jar, JSF to webapp, więc pozostaje oba projekty spiąć jako jeden w innym module - ear. A instalację pozostawić, albo wtyczce do M2, albo po prostu z poziomu Ant. Muszę to opisać dokładniej...Stay tuned!

    OdpowiedzUsuń
  3. Witam,

    rzeczywiscie, przykladowy projekt JsfJpa startuje w jedną chwile i cieszy mnie jego obecność tym bardziej że jestem żywo zainteresowany tą temetyką. Postanowiłem nauczyć się JSF i JPA w drodze rozwijania tego przykładu, no i niestety poległem w przedbiegach. To co zrobiłem to zastąpiłem wywołanie getUser() w klasie UserManager na:

    private Wuser getUser() {
    return (new.UserPersistenceServiceBean()).getUserByName(username);
    }

    i dodałem klasę:

    public class UserPersistenceServiceBean {
    @PersistenceContext private EntityManager em;

    public Wuser getUserByName( String userName ) {
    try {
    return (Wuser)em.createNamedQuery("Wuser.findByUsername").setParameter("username", userName).getSingleResult();
    } catch( NoResultException nrExc ) {
    return null;
    }
    }
    }

    i już wystarczyło żeby przestało działać:( tzn. dostaje NullPointerException z metody getUserByName(...), zapewne dlatego że nie została wstrzyknięta zależność EntityManager i zmienna em jest null. Niestety mam takie zjawiska zawsze jak prubuje kożystać z adnotacji. Może wiesz Jacku (albo ktoś inny) co partole?

    OdpowiedzUsuń
  4. Witaj Mariusz!

    Zanim dostaniesz prezent noworoczny w postaci rozwiązania Twojego problemu, dzięki wielkie za pytanie. Jeśli się nie pomyliłem (a nie zaglądałem do żadnej specyfikacji, a jedynie polegam na spostrzeżeniach) to wstrzeliwanie zależności nie działa dla zwykłych klas, które nie uczestniczą w rozwiązaniach JSF, EJB3 czy podobne (nie mam nawet pomysłu, czy pozostało coś jeszcze ;-))

    W Twoim przykładzie klasa UserPersistenceServiceBean była nikim w sensie specyfikacji Java EE 5, tj. nie była komponentem zarządzanym w sensie JSF, nie była komponentem EJB, po prostu nikim - taką sobie zwykłą klasą w Javie. Kiedy uaktywniłem jej obecność poprzez zadeklarowanie jej jako komponent zarządzany wszystko ruszyło. Przez długi czas się nad tym głowiłem, aż znalazłem rozwiązanie (z drugiej strony tworzenie klasy sugeruję pozostawić kontenerowi IoC, w naszym przypadku JSF, dla łatwiejszego później utrzymania aplikacji).

    Rozwiązanie przedstawia się następująco.

    W pliku faces-config.xml definiujemy nowy komponent zarządzany - userPersistenceService

    <managed-bean>
    <managed-bean-name>userPersistenceService</managed-bean-name>
    <managed-bean-class>enterprise.jsf_jpa_war.UserPersistenceServiceBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>

    a ten już istniejący - usermanager rozbudowujemy o właściwość userPersistenceService, tj.

    <managed-bean>
    <managed-bean-name>usermanager</managed-bean-name>
    <managed-bean-class>enterprise.jsf_jpa_war.UserManager</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
    <property-name>userPersistenceService</property-name>
    <value>#{userPersistenceService}</value>
    </managed-property>
    </managed-bean>

    W klasie UserManager dodajemy jednego settera i modyfikujemy getUser() tak, że ostatecznie klasa wygląda następująco:

    private Wuser getUser() {
    return userPersistenceService.getUserByName(username);
    }

    private UserPersistenceServiceBean userPersistenceService;

    public void setUserPersistenceService(UserPersistenceServiceBean userPersistenceService) {
    this.userPersistenceService = userPersistenceService;
    }

    Klasa UserPersistenceServiceBean pozostaje bez zmian - tak, jak zaproponowałeś.

    Uruchamiając zmodyfikowany przykład wszystko działa - zero NullPointerException (!)

    Powodzenia!

    Jacek

    OdpowiedzUsuń
  5. Witaj Jacek, witajcie wszyscy,

    szczęśliwego nowego roku - miłych prezentów noworocznych, takich jak ten od Jacka. Dzięki.

    Rzeczywiście działa, rzeczywiście IoC jest fajne. Jak można by mniemać z nazwy klasy UserPersistenceServiceBean planowałem zrobić z tego sesyjny EJB z interfejsem UserPersistenceService. Po wdrożeniu poprawek Jacka postanowiłem więc przeć dalej. Dodałem więc adnotacje @EJB

    @EJB
    private UserPersistenceService userPersistenceService;

    do odpowiedniej deklaracji w klasie UserManager, wywaliłem dependency injection

    <managed-property>
    <property-name>userPersistenceService</property-name>
    <value>#{userPersistenceService}</value>
    </managed-property>


    dodałem adnotacje @Stateless do klasy UserPersistenceServiceBean implements UserPersistenceService, dodałem interfejs UserPersistenceService adnotowany @Local i... dostałem wyjątek w trakcie startupu:

    Deploying application in domain failed; Error loading deployment descriptors for module [JsfJpa] -- Cannot resolve reference Unresolved Ejb-Ref enterprise.jsf_jpa_war.UserManager/userPersistenceService@jndi: @null@dao.UserPersistenceService@Session@null

    ...o jaki deskryptor chodzi?

    OdpowiedzUsuń