Embeddable EJB 3.1 z GlassFish 3.1 i NetBeans IDE 7.0

Z Jacek Laskowski - Wiki Projektanta Java EE

Rozdział 22. Embeddable Usage specyfikacji Enterprise JavaBeans (EJB) 3.1 opisuje nową cechę specyfikacji, która pozwala na uruchomienie kontenera EJB i zarządzanych przez niego komponentów EJB poza serwerem aplikacyjnym Java EE 6 - jedynie na poziomie środowiska Java SE. W ten sposób autorzy specyfikacji przewidują (a my programiści im wierzymy) uproszczenie procesu testowania, przetwarzania wsadowego (w którym użycie transakcji jest kluczowe) czy użycie EJB w samodzielnych aplikacjach desktopowych. Innymi słowy, mamy wszystko, co oferuje kontener EJB 3.1 bez konieczności uruchamiania pełnego serwera aplikacyjnego Java EE, którego sama konfiguracja uruchomieniowa mogła przyprawić o ból głowy.

Jest to duża zmiana w stosunku do poprzednich wersji specyfikacji EJB, które nie dosyć, że wymagały pełnego serwera aplikacyjnego, to były tak nietrywialne w użyciu (EJB 2.1 i starsze), że zwykło się je marginalizować na rzecz rozwiązań alternatywnych - najczęściej na korzyść zestawu Spring Framework i Hibernate (zresztą, jak wielu z nas pamięta, to właśnie bolączki przy pracy z EJB 1.1 sprowokowały do powstania Spring Framework).

W trybie wbudowanym (zanurzonym?, ang. embedded) odpowiedzialność za uruchomienie kontenera EJB oraz wskazanie komponentów EJB do uruchomienia spoczywa na programiście - specyfikacja EJB 3.1 określa dedykowane API w pakiecie javax.ejb.embeddable. Na chwilę obecną nie znajdziemy tam wiele (pod względem liczby klas), bo udostępnia się jedynie javax.ejb.embeddable.EJBContainer, ale owe "tylko tyle" wystarczy, aby móc uruchomić pełnoprawny kontener EJB 3.1 z usługami zarządzania (wstrzykiwania/przekazywania) zależnościami, zarządzanie transakcjami, bezpieczeństwo, utrwalanie danych, zarządzanie stanem i inne usługi, które sprawiały, że wybór serwera aplikacyjnego (często wyłącznie celem użycia kontenera EJB) był uzasadniony.

W tym artykule skorzystamy z referencyjnej implementacji dla specyfikacji Java EE 6, którym jest GlassFish 3.0.1 (którego podmienimy na najnowszą rozwojową wersję 3.1-b35) oraz najnowszą, rozwojową wersję NetBeans IDE 7.0 (z dnia 15.12.2010). Poza NetBeans IDE wszystkie składowe zostaną pobrane ze zdalnych repozytoriów przez Apache Maven i właśnie z tego powodu, w trakcie pierwszego uruchomienia testów, podłączenie do Sieci jest niezbędne.

Kompletny projekt jest dostępny jako nb7-ejb31-gf31-embeddable-ejb.zip.

Spis treści

Utworzenie projektu ziarna EJB - calculator-ejb

Dobrze jest korzystać z efektów pracy już wykonanej i nie inaczej będzie z naszą nauką. Podeprzemy się artykułem EJB 3.1 z OpenEJB 3.1 i NetBeans IDE 7.0, w którym znajdziesz, jak stworzyć bezstanowe ziarno sesyjne bez dedykowanego typu dla interfejsu biznesowego (użycie nowej cechy EJB 3.1 - @LocalBean).

Wykonaj kroki opisane w sekcji Stworzenie projektu calculator-ejb oraz Stworzenie bezstanowego ziarna sesyjnego EJB - Calculator.

Można również pobrać gotowy projekt nb7-ejb31-openejb-no-interface-view.zip i rozpakować do odpowiedniego katalogu, po czym wykonać import projektu do NetBeans.

Stworzenie projektu testującego - calculator-test

Stwórz osobny projekt testujący calculator-test, którego celem jest skorzystanie z uruchomienia wbudowanego kontenera EJB 3.1 do przetestowania naszego komponentu EJB - Calculator z projektu calculator-ejb. W ten sposób oddzielamy projekty i umożliwiamy testowanie ziarna EJB w ramach różnych kontenerów EJB 3.1 (tym razem GlassFish, ale w kolejnych artykułach dowiesz się o innych, chociażby Apache OpenEJB 3.1, JBoss AS 6 czy IBM WebSphere Application Server 8).

Wciśnij Cmd+Shift+N i z kategorii Maven wybierz Java Application.

Plik:nb7-embeddable-ejb31-gf31-new-project-java-app.png

Podaj dane projektu. Zwróć uwagę na pakiet - pl.jaceklaskowski.jee6.calculator (domyślnie pl.jaceklaskowski.jee6.calculatortest).

Plik:nb7-embeddable-ejb31-gf31-java-app-name-and-location.png

Wciśnij przycisk Finish.

Po chwili, w widoku Projects pojawi się kolejny projekt calculator-test.

Plik:nb7-embeddable-ejb31-gf31-calculator-test.png

Deklaracja zależności projektowych

Projekt calculator-ejb

Z menu kontekstowego Test Dependencies wybierz Add Dependency...

Plik:nb7-embeddable-ejb31-gf31-add-dependency.png

Przejdź do zakładki Open Projects (domyślnie wybrana jest Search). Wskaż na projekt calculator-test.

Plik:nb7-embeddable-ejb31-gf31-add-dep-open-projects.png

Z nieznanych mi powodów, zależność nie jest w pełni uzupełniona poprawnie i zawiera ${project.groupId} oraz ${project.version}. Jeśli groupId oraz version projektu calculator-test oraz calculator-ejb odpowiadają sobie, to nic nie szkodzi, ale proponuję zamienić na pl.jaceklaskowski.jee6 oraz 1.0-SNAPSHOT, odpowiednio. Skonsultuj pom.xml dla projektu calculator-ejb (nie wiesz, o czym tutaj się pisze? Skontaktuj się z autorem).

Plik:nb7-embeddable-ejb31-gf31-add-dep-open-projects-no-vars.png

Zatwierdź przyciskiem Add.

Test Dependencies powinna zawierać dwie pozycje - calculator-ejb oraz junit.

Plik:nb7-embeddable-ejb31-gf31-test-deps-2-deps.png

Repozytorium mavenowe glassfish-repository

W katalogu Project Files znajdziesz plik pom.xml.

Plik:nb7-embeddable-ejb31-gf31-pom.xml.png

Otwórz go.

Poniżej sekcji (znacznika) dependencies wklej poniższą sekcję repositories:

<repositories>
  <repository>
    <id>glassfish-repository</id>
    <url>http://download.java.net/maven/glassfish</url>
  </repository>
</repositories>

Zapisz zmianę i zamknij plik.

org.glassfish/javax.ejb/3.1-b35

Z menu kontekstowego Test Dependencies wybierz Add Dependency... i podaj dane zależności org.glassfish/javax.ejb/3.1-b35.

Zwróć uwagę, że zależność jest na poziomie (Scope) provided (potrzebujących wyjaśnienia uprasza się o kontakt z autorem w ramach pogłębiania znajomości :)).

Plik:nb7-embeddable-ejb31-gf31-add-deps-javax-ejb.png

Zatwierdź wybór przyciskiem Add.

org.glassfish.extras/glassfish-embedded-all/3.1-b35

Kolejną zależnością jest główna zależność pozwalająca na użycie wbudowanego kontenera EJB 3.1 - org.glassfish.extras/glassfish-embedded-all/3.1-b35.

Podobnie, jak wcześniej, wybierz Test Dependencies, później Add Dependency... i podaj dane zależności.

Plik:Nb7-embeddable-ejb31-gf31-add-deps-glassfish-embedded-all.png

Zatwierdź wybór przyciskiem Add.

JUnit 4.8.2

Jakkolwiek już mamy zdefiniowaną zależność projektową JUnit 3.8.1 to możliwości JUnit 4.8.2 są nie do przecenienia (przede wszystkim adnotacje) i to właśnie tę wersję użyjemy - zastąpimy istniejącą, starszą na nowszą.

Wybierz Add Dependency... z menu kontekstowego Test Dependencies i zdefiniuj zależność junit/junit/4.8.2 w wybrany przez siebie sposób - podając bezpośrednio Group ID, Artifact ID i Version, jak poprzednio lub korzystając z wyszukiwarki poniżej podając junit, 4.8.2 w polu Query i wybierając właściwy element.

Plik:Nb7-embeddable-ejb31-gf31-add-deps-junit.png

Zatwierdź wybór przyciskiem Add.

Aktualna zależność JUnit w wersji niższej zostanie podmieniona na wyższą.

Podsumowanie - Test Dependencies oraz pom.xml

Projekt poprawnie zdefiniowany zależnościowo powinien prezentować się jak poniżej:

Plik:Nb7-embeddable-ejb31-gf31-deps.png

Plik konfiguracyjny pom.xml powinien wyglądać jak poniżej z jedną zmianą - zdefiniowaną zmienną glassfish.version tak, aby uprościć późniejsze zmiany, kiedy pojawi się nowsza wersja.

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>pl.jaceklaskowski.jee6</groupId>
  <artifactId>calculator-test</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>calculator-test</name>
  <url>http://www.jaceklaskowski.pl/wiki</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <glassfish.version>3.1-b35</glassfish.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>pl.jaceklaskowski.jee6</groupId>
      <artifactId>calculator-ejb</artifactId>
      <version>1.0-SNAPSHOT</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.ejb</artifactId>
      <version>${glassfish.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish.extras</groupId>
      <artifactId>glassfish-embedded-all</artifactId>
      <version>${glassfish.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>glassfish-repository</id>
      <url>http://download.java.net/maven/glassfish</url>
    </repository>
  </repositories>
</project>

Utworzenie klasy testującej - CalculatorTest

Z menu kontekstowego Test Packages wybierz New > Other... i dalej w kategorii JUnit wybierz JUnit Test (niestety Test for Existing Class jest jedynie dla klas ze źródłami w bieżącym projekcie).

Plik:Nb7-embeddable-ejb31-gf31-new-file-junit-test.png

Wciśnij przycisk Next >.

Podaj dane klasy testowej jak poniżej z opcjonalnym odznaczeniem opcji w Generated Code oraz Generated Comments - po prostu i tak nie będziesz z nich korzystał.

Plik:nb7-embeddable-ejb31-gf31-new-file-junit-test-name-and-location.png

Zatwierdź zmiany przyciskiem Finish.

Zmień zawartość klasy na poniższą.

package pl.jaceklaskowski.jee6.calculator;
 
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.NamingException;
import org.junit.After;
import org.junit.Before;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import org.junit.Test;
import static org.junit.Assert.*;
 
/**
 * @author Jacek Laskowski
 */
public class CalculatorTest {
 
  EJBContainer ejbContainer;
  Context ctx;
 
  @Before
  public void setUp() {
    // Uruchomienie wbudowanego kontenera EJB 3.1
    ejbContainer = EJBContainer.createEJBContainer();
 
    // Dostanie się do kontekstu JNDI
    ctx = ejbContainer.getContext();
  }
 
  @Test
  public void testMultiply() throws Exception {
    // Dane testowe
    int x = 2;
    int y = 4;
 
    // Wyszukanie ziarna Calculator w kontekście java:global
    Calculator calculator = (Calculator) ctx.lookup("java:global/classes/Calculator");
 
    // Uruchomienie metody ziarna EJB
    int result = calculator.multiply(x, y);
 
    // Sprawdzenie poprawności wykonania metody biznesowej
    int expected = x * y;
    assertEquals(expected, result);
  }
 
  @After
  public void tearDown() {
    try {
      ctx.close();
    } catch (NamingException ex) {
      Logger.getLogger(CalculatorTest.class.getName()).log(Level.SEVERE, null, ex);
    }
    ejbContainer.close();
  }
}

Testy, testy, testy - uruchomienie CalculatorTest

Wybierz Test File z menu kontekstowego klasy testowej CalculatorTest lub po prostu wciśnij kombinację klawiszy Cmd+F6 (zakładając, że projekt jest głównym projektem - jego nazwa jest wtedy wytłuszczona).

Plik:nb7-embeddable-ejb31-gf31-test-file.png

W widoku Test Results pojawi się poprawnie wykonany test CalculatorTest.

Plik:nb7-embeddable-ejb31-gf31-test-results.png

Gratulacje!

UWAGA: Czasami konieczne jest najpierw uruchomienie testu w ramach pełnego uruchomienia testowego w projekcie, tj. poprzez wspomniane Cmd+F6 lub Run > Test Project (calculator-test). Dopiero wtedy możliwe jest uruchomienie testu pojedynczo i to często również wymaga zmiana nazwy JNDI. U Ciebie może być inaczej.

Plik:Nb7-embeddable-ejb31-gf31-test.png
Osobiste