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.

