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.
Kompletny projekt ear-ejb-beanname dostępny jest do pobrania jako ear-ejb-beanname.zip.

