Java EE 6 står i tilgængelighedens tegn

I denne artikel fortæller Kasper Sørensen om et par af de helt nye ting fra Java EE 6: Bean Validation og Contexts and Dependency Injection. Disse udvidelser til Java EE platformen har stor indflydelse på hele arkitekturen. Kort sagt vil det få stor betydning for måden man komponerer moduler og styrer disses livscyklus – begge dele på tværs af alle tiers i Java EE platformen.
Den 10. december udkom Java Enterprise Edition (Java EE) 6 specifikationen, samt Sun’s reference implementation, Glassfish. Der går typisk altid lidt tid før de andre store Java EE produkter kommer med på nyeste version, men i mellemtiden kan vi med rimelighed begynde at gøre os nogle forventninger. Jeg vil i denne artikel gennemgå et par ting, som jeg tror bliver helt centrale for Java EE 6.

Java EE 6 bliver nok ikke en lige så omfattende omvæltning som Java EE 5 var ifht. J2EE 1.4. Dermed ikke sagt at der ikke er store ændringer i udsigt – for det er der i høj grad! Der er tale om en videreudvikling af Enterprise Edition, som får komponenterne i arkitekturen til at hænge meget bedre sammen og får forenet de ting, som tidligere har båret præg af lidt for mange uafhængige forsøg på at løse de samme problemer. Ændringerne er altså ikke så omvæltende, men det er mit bud, at Java EE 6 kommer til at tilføje EE-platformen mere værdi end tidligere releases har gjort – netop fordi der fokuseres på at få delene til at spille bedre sammen.

Bean Validation

Et eksempel på denne forenede tilgang til problemerne er Bean Validation. Bean Validation er en nyhed i Java EE 6, som tilbyder data-validering på en ensartet måde i hele arkitekturen (eksempelvis ved input fra brugeren, som en del af batch-job eller før indsættelse i databasen). Det er nok ingen overraskelse, at Bean Validation har haft indflydelse på mange områder: Servlets, EJB, JavaServer Faces, JAX-WS mfl. Hvis man vil benytte Bean Validation allerede nu, er det faktisk muligt – der eksisterer en reference implementation, nemlig Hibernate Validator 4.x.

Dependency Injection

En anden meget spændende ting i Java EE 6 er henholdsvis Contexts and Dependency Injection for the Java EE Platform (tidl. kendt som “WebBeans”) og Dependency Injection for Java. Begge dele inkluderes i Java EE 6 og handler om, hvordan man tilgår komponenter i Java EE – eller rettere sagt, hvordan de gøres tilgængelige. Som mange nok ved, er Dependency Injection et mønster for, hvordan man “injecter” afhængigheder istedet for at en afhængighed skal “slås op” – det sparer os for en masse kode og det gør administrationen af afhængigheder imellem komponenter meget lettere at håndtere.

I Java EE 6 bliver Dependency Injection en helt central mekanisme i platformen. En af de grundlæggende principper bag Dependency Injection i EE 6 er “løs kobling med typestærk kvalificering”. Løs kobling i denne forbindelse henviser til et velkendt princip for Dependency Injection: Hvis modul A skal bruge en instans af B, bør A ikke nødvendigvis kende hvilken konkret B der er tale om. Den bør heller ikke kende til B’s livscyklus. Dette princip opfordrer bl.a. til at man koder mod interfaces, så afhængigheder let kan udskiftes. Typestærk kvalificering henviser til behovet for at kunne foretage wiring af afhængigheder på en typestærk måde. Mange vil være bekendt med Spring Frameworks XML-baserede wiring, hvor man navngiver sine beans og benytter disse, når man injecter. Selvom denne XML-løsning giver os Dependency Injection, hvilket klart er et fremskridt, så er der mange problemer forbundet med den: Løsningen er ikke typestærk og kan derfor ikke compile-time checkes, hvorfor den er svær at vedligeholde/refaktorere.

Java EE 6’s løsning

I Java EE 6 er der derfor brugt såkaldte Qualifier-annotationer til at kvalificere, hvad der skal injectes. Hvis der kun findes én konkret implementation af typen vi vil injecte, behøves qualifiers ikke. Men antag at vi vil injecte noget (fx en service til registrering af ordrer) som der findes flere af (fx en synkron og en asynkron variant) – så skal vi bruge noget til at kvalificere dem, så vi kan skelne den ene fra den anden. Vi definerer derfor vores egne qualifier annotationer (fx. @Asynchronous og @Synchronous):

public interface OrderService { ... }

@Asynchronous
public class AsyncOrderService implements OrderService { ... }

@Synchronous
public class SynchronousOrderService implements OrderService {...}

Som sagt er de to annotationer i eksemplet ovenfor nogle vi selv har defineret. Disse annotationer skal yderligere annoteres med @Qualifier. Eksempelvis:

@Qualifier
@Retention(RUNTIME)
public @interface Asynchronous {
}

Når vi skal injecte en OrderService, skal vi kvalificere hvilken type vi er interesseret i. Vi benytter qualifier annotationerne til at foretage en løst koblet kvalificering, da vi ikke ønsker at referere stærkt til den ene eller anden implementation (de skal jo kunne udskiftes). Når vi injecter benyttes altid @Inject annotationen også:

public class MyBean {
  @Inject @Asynchronous
  OrderService orderService;

  public void myMethod() {
    // her kan jeg benytte "orderService"
  }
}

Hvis dette virker kompliceret, så husk: Vi skal kun benytte qualifier annotationer i de tilfælde hvor vi ØNSKER den løse kobling kombineret med udskifteligheden. Vi kan undlade dem fuldstændigt ved fx at injecte noget mere konkret (“mindre løst” koblet):

@Inject
AsyncOrderService asyncOrderService;

Noget der er exceptionelt lækkert ved EE 6 ifht. tidligere versioner er, at hvis man har brug for eksempelvis ServletContexten, en BeanValidator, EntityManager’en eller lign., så skal man “bare” injecte den. Det fører til, at man i høj grad kan vælge hvor tier-opdelt man ønsker sin applikation skal være. Det betyder også, at alting pludseligt er tilgængeligt. Hvor ofte har man ikke siddet og bandet over at man fx. ikke kunne trække et objekt ud af sessionen fordi HttpSession-objektet ikke var med i parameter-listen på den metode man sad og udviklede i? De dage er forbi

Context, Scope, Livscyklus

En af de nyskabende ting i Java EE 6’s Dependency Injection mekanisme er konceptet “kontekst”. Især i forhold til web-brugergrænseflader har kontekst-styring stor betydning. Nogle vil måske være vandt til at forstå begrebet kontekst som “objekters livscyklus”. Kort fortalt handler det om, at man i webprogrammering (mere eller mindre ubevidst) har haft at gøre med komponenter, der lever i forskellige kontekster – et HTTP-request, en brugers session, en wizard som brugeren udfylder, hele applikationen, en forretningsprocess eller lign. Nogle kontekster er altid til stede (eks. “application context” og “session context”), hvor andre skal startes og afsluttes, hvilket er tilfældet med den nye “conversation context”. Vi skal ikke længere kun forstå dependencies som globale moduler (i application context) Der er nu også mulighed for både at injecte og “producere” moduler i andre kontekster: En indkøbskurv kan eksempelvis sagtens injectes i en Stateless session bean, en conversation-scoped wizard kan sagtens tilgå session-scoped bruger-informationer og en stateless session bean kan envidere producere et request-scoped Ordre-objekt. Disse ting er som sådan naturlige nok, men tilgængelighed imellem objekter der lever i forskellige kontekster har typisk været temmelig bøvlet og endnu værre: Udsat for mange fejlkilder, da man typisk skulle lave et lookup i JNDI-træet, HttpSession-objektet, HttpServletRequest-objektet eller lign.

Perspektiver

Det er tydeligt, at mange af ideerne i EE 6 er baseret på henholdsvis Spring Framework og Google Guice ifht. Dependency Injection og JBoss Seam ifht. kontekst-begrebet. Hvis man vil prøve Java EE 6’s Dependency Injection mekanisme, vil jeg råde til at prøve sig frem med nyeste version af Glassfish serveren, hvor reference implementationen (JBoss Weld) er inkluderet.