Element beanName w @EJB do rozróżnienia deklaracji ziaren EJB

Z Jacek Laskowski - Wiki Projektanta Java EE

Już jakiś czas temu spotkałem się z pytaniem odnośnie deklarowania zależności do dwóch różnych ziaren EJB, które realizują ten sam interfejs biznesowy. Nadeszła pora na rozwiązanie tej kwestii i tym samym powrót do ponownego rozpoznawania specyfikacji EJB3 z przykładami.

Rozpoczniemy od stworzenia aplikacji korporacyjnej z EJB3 i JSF 1.2. Aplikacja będzie składała się z ziarna JSF oraz dwóch bezstanowych ziaren sesyjnych EJB, które realizują ten sam interfejs biznesowy. Korzystając z adnotacji @EJB określimy zależności ziarna JSF do obu ziaren EJB i od wyboru użytkownika będzie zależało, którą metodę biznesową wywołamy - tą realizowaną przez "pierwsze" ziarno EJB, czy "drugie".

Zaczynam od utworzenia interfejsu biznesowego - Slownik (interfejs pl.jaceklaskowski.beanname.ejb.Slownik).

package pl.jaceklaskowski.beanname.ejb;

public interface Slownik {
    String przetlumacz(String slowo);
}

Kolejnym elementem naszej aplikacji będzie realizacja interfejsu przez bezstanowe ziarno sesyjne EJB - PolskoAngielskiSlownik (klasa pl.jaceklaskowski.beanname.ejb.PolskoAngielskiSlownik).

package pl.jaceklaskowski.beanname.ejb;

import javax.ejb.Stateless;

@Stateless
public class PolskoAngielskiSlownik implements Slownik {

    public String przetlumacz(String slowo) {
        return "pl->ang: " + slowo;
    }

}

Drugim, bezstanowym ziarnem EJB w aplikacji będzie AngielskoPolskiSlownik (klasa pl.jaceklaskowski.beanname.ejb.AngielskoPolskiSlownik).

package pl.jaceklaskowski.beanname.ejb;

import javax.ejb.Stateless;

@Stateless
public class AngielskoPolskiSlownik implements Slownik {

    public String przetlumacz(String slowo) {
        return "en->pl: " + slowo;
    }

}

Elementem widocznym dla użytkownika w naszej korporacyjnej aplikacji będzie aplikacja internetowa zbudowana w oparciu o JSF 1.2.

Tworzymy ziarno zarządzane JSF - SlownikBean (klasa pl.jaceklaskowski.beanname.faces.SlownikBean), w którym wykorzystuję utworzone wcześniej ziarna EJB.

package pl.jaceklaskowski.beanname.faces;

import javax.ejb.EJB;
import pl.jaceklaskowski.beanname.ejb.Slownik;

public class SlownikBean {

    @EJB
    Slownik polskoAngielskiSlownik;
    
    @EJB
    Slownik angielskoPolskiSlownik;

    private String jezyk;
    
    private String slowo;
    
    private String tlumaczenie;
    
    public void wywolajPrzetlumacz() {
        if (jezyk.equalsIgnoreCase("pl")) {
            angielskoPolskiSlownik.przetlumacz(slowo);
        } else {
            polskoAngielskiSlownik.przetlumacz(slowo);
        }
    }

    public String getSlowo() {
        return slowo;
    }

    public void setSlowo(String slowo) {
        this.slowo = slowo;
    }

    public String getJezyk() {
        return jezyk;
    }

    public void setJezyk(String jezyk) {
        this.jezyk = jezyk;
    }

    public String getTlumaczenie() {
        return tlumaczenie;
    }

    public void setTlumaczenie(String tlumaczenie) {
        this.tlumaczenie = tlumaczenie;
    }
}

Za pomocą adnotacji @EJB serwer przekaże referencje do odpowiedniego ziarna EJB bazując na...właśnie zastanówmy się, w jaki sposób serwer miałby zdecydować, które ziarno EJB odpowiada, któremu z pól. Do dyspozycji jest typ interfejsu biznesowego (w tym przypadku jest to Slownik) i tyle. Zbyt mało, aby można było zdecydować o wyborze ziarna dla poszczególnych pól.

Jeśli spróbujemy wdrożyć taką aplikację na serwer GlassFish aplikacja zostanie odrzucona z komunikatem:

 Deploying application in domain failed; Error loading deployment descriptors for module [ear-ejb-beanname] -- 
 Cannot resolve reference Unresolved Ejb-Ref pl.jaceklaskowski.beanname.faces.SlownikBean/polskoAngielskiSlownik@jndi: @null@pl.jaceklaskowski.ejb.Slownik@Session@null 
 because there are 2 ejbs  in the application with interface pl.jaceklaskowski.ejb.Slownik

Dokładnie jak możnaby tego oczekiwać.

Z pomocą przychodzi element beanName w adnotacji @EJB oraz name w @Stateless. Jeśli wartości obu elementów będą zgodne nastąpi przypisanie ziarna do tak udekorowanego pola.

Zmodyfikujmy zatem nasze ziarna, aby zawierały dedykowane nazwy za pomocą elementu name w adnotacji @Stateless.

package pl.jaceklaskowski.beanname.ejb;

import javax.ejb.Stateless;

@Stateless(name="PolskoAngielskiSlownik")
public class PolskoAngielskiSlownik implements Slownik {

    public String przetlumacz(String slowo) {
        return "pl->ang: " + slowo;
    }

}

oraz

package pl.jaceklaskowski.beanname.ejb;

import javax.ejb.Stateless;

@Stateless(name="AngielskoPolskiSlownik")
public class AngielskoPolskiSlownik implements Slownik {

    public String przetlumacz(String slowo) {
        return "en->pl: " + slowo;
    }

}

Ostateczne zmiany wprowadzamy w ziarnie JSF.

package pl.jaceklaskowski.beanname.faces;

import javax.ejb.EJB;
import javax.faces.event.ActionEvent;
import pl.jaceklaskowski.beanname.ejb.Slownik;

public class SlownikBean {

    @EJB(beanName="PolskoAngielskiSlownik")
    Slownik polskoAngielskiSlownik;
    
    @EJB(beanName="AngielskoPolskiSlownik")
    Slownik angielskoPolskiSlownik;

    private String jezyk = "pl";
    
    private String slowo;
    
    private String tlumaczenie;
    
    public void wywolajPrzetlumacz(ActionEvent event) {
        if (jezyk.equalsIgnoreCase("pl")) {
            setTlumaczenie(angielskoPolskiSlownik.przetlumacz(slowo));
        } else {
            setTlumaczenie(polskoAngielskiSlownik.przetlumacz(slowo));
        }
    }

    public String getSlowo() {
        return slowo;
    }

    public void setSlowo(String slowo) {
        this.slowo = slowo;
    }

    public String getJezyk() {
        return jezyk;
    }

    public void setJezyk(String jezyk) {
        this.jezyk = jezyk;
    }

    public String getTlumaczenie() {
        return tlumaczenie;
    }

    public void setTlumaczenie(String tlumaczenie) {
        this.tlumaczenie = tlumaczenie;
    }
}

Przykładowa strona JSF - slownik.jsp - mogłaby wyglądać następująco:

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Słownik</title>
    </head>
    <body>
        <f:view>
            <h1><h:outputText value="Słownik" /></h1>
            <h:form>
                <h:selectOneRadio value="#{slownik.jezyk}" required="true">
                    <f:selectItem itemLabel="Słownik angielsko-polski" itemValue="pl" />
                    <f:selectItem itemLabel="Słownik polsko-angielski" itemValue="en" />
                </h:selectOneRadio>
                <br>
                <h:inputText value="#{slownik.slowo}" />
                <h:outputText value="#{slownik.tlumaczenie}" />
                <br>
                <h:commandButton value="Przetłumacz" actionListener="#{slownik.wywolajPrzetlumacz}"/>
            </h:form>
        </f:view>
    </body>
</html>

Brakującym, acz faktycznie niewiele wnoszącym do tematu, elementem aplikacji jest plik konfiguracyjny JSF - faces-config.xml. Przedstawiam go dla kompletności materiału.

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
    <managed-bean>
        <managed-bean-name>slownik</managed-bean-name>
        <managed-bean-class>pl.jaceklaskowski.beanname.faces.SlownikBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
</faces-config>

Uruchomienie aplikacji na serwerze aplikacyjnym Java EE 5 to wykonanie strony JSF slownik.jsp, która pozwala użytkownikowi wybrać dostępny słownik, zgodnie z którym zostanie przetłumaczone podane słowo.

Grafika:ejbname20080117_235952.jpg

Kompletny projekt ear-ejb-beanname dostępny jest do pobrania jako ear-ejb-beanname.zip.

Osobiste