Nyheder i EJB 3.1 – mere simpelt og flere nye features

Fra EJB 2.x til EJB 3.0 lagde man stor vægt på en deklarativ tilgang til arbejdet med Enterprise Java Beans og forenklede dermed arbejdet med EJB’ere. Med EJB3.1 har man implementeret yderligere simplificeringer og tilføjet nye features, der alle kan bruges i Java EE 6 applikationer. Her er de vigtigste:

– No-interface view for lokale EJB’ere
– Ny Singleton bean der gør det nemmere at dele tilstand
– Asynkrone kald af session beans
– EJB Timer udtryk der nu er kalender-baserede
– Automatisk genererede EJB Timere
– En global JNDI navnesyntaks, der er portable
– Pakning af EJB komponenter direkte i .war filer uden en ejb.jar fil

I denne artikel vil jeg redegøre for nogle af de største nyheder, der er kommet med EJB 3.1. Hovedformålet med EJB 3.0 var at gøre det lettere at arbejde med EJB’ere, så udviklere kunne bruge mere tid på det væsentligste – forretningslogikken – og mindre tid på konfiguration og opsætning. Det, synes jeg, er lykkedes et godt stykke hen ad vejen, dog var det på bekostning af, at der i EJB 3.0 ikke blev tid til at opfylde mange ønskede faciliteter. Det var således nogle af disse mangler, som der blev sat fokus på i EJB 3.1. Jeg ser på disse nye features og anvender udelukkende annotationer i eksemplerne. Alle annotationer kan naturligvis også skrives programmatisk eller konfigureres i en deployment descriptor.

No-interface view for lokale EJB’ere

I EJB 3.1 behøver man ikke at udstille et local business interface for lokale beans. Hvis ikke man eksplicit laver et interface og således anvender no-interface view af en bean, så udstilles alle public metoder til lokale klienter, der kører i samme JVM. Her skal man dog bemærke, at alle public metoder udstilles – også callbackmetoder og superklassernes public metoder. Derfor bør det kun være forretningsmetoder, der erklæres public. Hvis bean’en udstiller andre interfaces (f.eks. remote), så skal den annoteres med @LocalBean, hvis man samtidig ønsker et no-interface view.

Man får en reference til en no-interface view bean ved brug af dependency injection eller JNDI opslag.

Dependency injection:

@EJB CalculatorBean calc;

JNDI opslag:

@Ressource SessionContext ctx;
...
CalculatorBean calc = (CalculatorBean) ctx.lookup("calculator");

Singleton bean

Stateless og stateful session beans er nu udvidet med en ny type kaldet singleton beans, der erklæres ved annotationen @Singleton. Ved erklæring af en singleton bean garanterer serveren, at der kun oprettes en instans af denne. Dennes tilstand deles således på tværs af hele applikationen. Der gælder altså følgende:

@EJB MySingleton singleton1;

@EJB MySingleton singleton2;
...
if (singleton1.equals(singleton1)) { // Returnerer altid true
    ...
}
...
if (singleton1.equals(singleton2)) { // Returnerer også altid true
    ...

En Singleton beans livscyklus starter, når den instantieres af containeren og ender, når applikationen terminerer. Default er det containeren, der afgør, hvornår en Singleton bean instantieres. Sættes en @Startup annotation på klassen, så tvinges containeren til at instantiere bean’en ved opstart af applikationen.

Sker der et crash af serveren, så husker en Singleton bean ikke sin tilstand!

Singleton beans kan f.eks. være meget anvendelige ved implementering af en fælles cache eller til at indeholde applikations-start/stop logik.

Asynkrone kald af session beans

Tidligere var det kun muligt at lave synkrone kald af session beans. Klienten måtte vente på, at bean’en returnerede, før klienten igen kunne eksekvere. Det er med EJB 3.1 nu muligt at kalde session beans (med local business interface, remote business interface eller no-interface) metoder asynkront ved at annotere dem @Asynchronous. Sættes annotationen på klassen bliver alle business interface metoderne gjort asynkrone. En metode annoteret med @Asynchronous skal returnere en af flg. typer

  1. void med fire and forget semantik.
    Disse metoder må ikke smide application exceptions.
  2. Future<V> med forventning om en returværdi.
    Disse metoder må gerne smide exceptions og kan afbrydes.
    Klienten får fat i returværdien med typen V ved at kalde en af Futures get() metoder. Specifikationen anbefaler, at klienten kalder get() på alle de Future<V> objekter, som den modtager, med mindre at det lykkes klienten at afbryde kaldet ved at invokere Future<V>.cancel() metoden.
    Hvis containeren kaster en EJBException (systemfejl), så afbrydes kaldet og klienten må evt. forsøge at kalde igen. Hvis der opstår en exception ved udførelsen af den asynkrone metode (applikationsfejl), kan denne exception tilgås af klienten ved metoden getCause() på den ExecutionException som alle Future<V>’s get() metoder kan kaste.

Her er et eksempel på implementering af en asynkron metode.

@Asynchronous
public Future<V> myAsyncMethod() {
    …
    return AsyncResult<String>(”Hej fra myAsyncMethod”);
}

AsyncResult er en bekvem implementation af Future<V> interfacet. Objektet som den modtager i sin konstruktør returneres til klienten, når den kalder en af get() metoderne på Future<V> objektet.

En asynkron metode deltager ikke i en allerede igangværende transaktion, men laver en ny transaktion til kaldet. Derfor er semantikken for transaktionsattributten REQUIRED nøjagtig den samme som REQUIRED_NEW!

Timers

Timers kan anvendes af alle typer session beans med undtagelse af stateful session beans! Med EJB 3.1 kan man nu anvende Calendar-baserede timere i stedet for at skulle angive tiden i milisekunder. Desuden kan en timer nu skabes automatisk med @Schedule annotationen på en metode. En stor fordel ved dette er, at man ikke længere skal stole på, at en metode bliver kaldt programmatisk for at starte timeren. @Schedule har en del Calendar baserede attributter, der fortæller, hvornår en timer skal startes, samt hvor ofte den skal kaldes. De kalenderbaserede attributter er følgende: year, dayOfWeek, month, dayOfMonth, hour, minute og second.

Her er et par eksempler med anvendelse af nogle af attributterne:

//Kør et batchjob hver onsdag morgen kl. 3:15
@Schedule(minute=”15”, hour=”3”, dayOfWeek=”Mon-Fri”)

//Send en frokostnotifikation dagligt kl. 11.25, dog kl. 11.00 om fredagen
@Schedules({
    @Schedule(hour=”12”, dayOfWeek=”Mon-Thu”),
    @Schedule(hour=”11”, dayOfWeek=”Fri”)
})

Som det ses af sidste eksempel, kan man tilføje mange timere til en metode. Timere overlever genstart af serveren, crash osv. Ønsker man ikke, at timeren skal være persistent og have denne egenskab, så kan man tilføje attributten persistent=false.

Global JNDI navnesyntaks

Java EE Standard Specifikationen (for EE 6 versionen) beskriver et standardiseret globalt JNDI namespace, der gør det muligt at få en reference til en Java EE komponent eller ressource. I tidligere versioner var JNDI navnet serverspecifikt og ikke portabelt. I Java EE 6 versionen, er det globale JNDI navn blevet standardiseret med følgende syntaks:

java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-quali­fied-interface-name>]

Pakning af EJB komponenter direkte i .war filer

Der er med Java EE 6 givet en ny mulighed for pakning af EJB komponenter direkte i .war filer. Tidligere skulle EJB komponenter pakkes i en x-ejb.jar fil og web komponenter i en y-web.war fil og disse to filer skulle endelig pakkes i en z-app.ear fil. I Java EE 6 kan EJB komponenter nu pakkes direkte i .war filer. Kompilerede EJB komponenter placeres i WEB-INF/classes folderen og EJB komponenter pakket i en .jar fil placeres i WEB-INF/lib folderen. En eventuel deployment descripter, ejb-jar.xml, placeres i WEB-INF folderen.

EJB komponenter, der er pakket i en .war fil, har samme krav til class loading som ikke-EJB komponenter i en .war fil. Det betyder f.eks. at en servlet pakket i en .war fil er garanteret synlig for en EJB komponent pakket i den samme .war fil og vice verca.

Her er et eksempel med en stateless bean med et no-interface view:

package com.acme;

@Stateless
public class FooBean {
     public void foo() { ... }
}

FooBean bliver pakket i en .war fil i WEB-INF/classes folderen svarende til sit pakkenavn. Der figurerer også en servlet i .war filen, der ser således ud:

webejb.war:
WEB-INF/classes/com/acme/FooServlet.class
WEB-INF/classes/com/acme/FooBean.class

Afrunding

EJB 3 simplificerede programmeringsmodellen markant. Modellen er blevet deklarativ – implementeret ved simple annotationer. Valget af defaultværdier, der følger konceptet “configuration by exception”, har gjort udviklingen med EJB 3 væsentlig lettere. En session bean er nu en almindelig POJO (Plain Old Java Object) med et interface, og med EJB 3.1 er lokale beans interface endda overflødiggjort!

De nye features beskrevet i denne artikel er fortsatte bestræbelser på at gøre det enklere at arbejde på Java EE platformen. Disse bestræbelser er blevet indfriet langt hen ad vejen, og EJB 3.x minder meget om Spring frameworket, både hvad angår den deklarative programmeringsmodel samt funktionaliteten.