Singleton Session Bean in Jakarta EE

1. Overzicht

Wanneer een enkele instantie van een Session Bean vereist is voor een bepaald gebruik, kunnen we een Singleton Session Bean gebruiken.

In deze tutorial gaan we dit onderzoeken aan de hand van een voorbeeld, met een Jakarta EE-applicatie.

2. Maven

Allereerst moeten we vereiste Maven-afhankelijkheden definiëren in het pom.xml.

Laten we de afhankelijkheden definiëren voor de EJB-API's en de ingesloten EJB-container voor implementatie van de EJB:

 javax javaee-api 8.0 leverde org.apache.openejb tomee-embedded 1.7.5 

De nieuwste versies zijn te vinden op Maven Central op JavaEE API en tomEE.

3. Soorten Session Beans

Er zijn drie soorten Session Beans. Laten we, voordat we Singleton Session Beans gaan verkennen, eens kijken wat het verschil is tussen de levenscycli van de drie soorten.

3.1. Stateful Session Beans

Een Stateful Session Bean handhaaft de gesprekstoestand met de cliënt die hij communiceert.

Elke client maakt een nieuw exemplaar van Stateful Bean aan en wordt niet gedeeld met andere clients.

Wanneer de communicatie tussen de client en de bean eindigt, wordt ook de Session Bean beëindigd.

3.2. Staatloze Sessiebonen

Een staatloze sessieboon onderhoudt geen enkele conversatiestatus met de cliënt. De bean bevat alleen de status die specifiek is voor de client tot de duur van de methode-aanroep.

Opeenvolgende methode-aanroepen zijn onafhankelijk in tegenstelling tot de Stateful Session Bean.

De container onderhoudt een pool van staatloze bonen en deze instanties kunnen worden gedeeld tussen meerdere klanten.

3.3. Singleton Session Beans

Een Singleton Session Bean handhaaft de status van de boon gedurende de volledige levenscyclus van de applicatie.

Singleton Session Beans zijn vergelijkbaar met Stateless Session Beans, maar er wordt slechts één exemplaar van de Singleton Session Bean gemaakt in de hele applicatie en wordt pas beëindigd als de applicatie wordt afgesloten.

Het enkele exemplaar van de bean wordt gedeeld door meerdere clients en kan gelijktijdig worden geopend.

4. Creëren van een Singleton Session Bean

Laten we beginnen met het maken van een interface ervoor.

Laten we voor dit voorbeeld de javax.ejb.Local annotatie om de interface te definiëren:

@Local openbare interface CountryState {List getStates (String country); void setStates (String country, List states); }

Gebruik makend van @Lokaal betekent dat de boon toegankelijk is binnen dezelfde applicatie. We hebben ook de mogelijkheid om gebruik te maken van javax.ejb.Remote annotatie waarmee we de EJB op afstand kunnen bellen.

Nu gaan we de implementatie-EJB-bean-klasse definiëren. We markeren de klas als een Singleton Session Bean door de annotatie javax te gebruiken.ejb.Singleton.

Laten we bovendien de boon markeren met de javax.ejb.Startup annotatie om de EJB-container te informeren om de boon bij het opstarten te initialiseren:

@Singleton @Startup openbare klasse CountryStateContainerManagedBean implementeert CountryState {...}

Dit wordt gretige initialisatie genoemd. Als we geen gebruik maken van @Opstarten, bepaalt de EJB-container wanneer de boon moet worden geïnitialiseerd.

We kunnen ook meerdere sessiebonen definiëren om de gegevens te initialiseren en de bonen in de specifieke volgorde te laden. Daarom gebruiken we, javax.ejb.DependsOn annotatie om de afhankelijkheid van onze boon van andere Session Beans te definiëren.

De waarde voor de @Hangt af van annotatie is een reeks namen van de namen van de Bean-klassen waarvan onze Bean afhankelijk is:

@Singleton @Startup @DependsOn ({"DependentBean1", "DependentBean2"}) openbare klasse CountryStateCacheBean implementeert CountryState {...}

We definiëren een initialiseren () methode die de bean initialiseert en er een callback-methode voor de levenscyclus van maakt met javax.annotation.PostConstruct annotatie.

Met deze annotatie wordt deze door de container aangeroepen bij instantiatie van de boon:

@PostConstruct public void initialize () {List states = new ArrayList (); states.add ("Texas"); states.add ("Alabama"); states.add ("Alaska"); states.add ("Arizona"); states.add ("Arkansas"); countryStatesMap.put ("Verenigde Staten", staten); }

5. Gelijktijdigheid

Vervolgens ontwerpen we het gelijktijdigheidsbeheer van Singleton Session Bean. EJB biedt twee methoden voor het implementeren van gelijktijdige toegang tot de Singleton Session Bean: door containers beheerde gelijktijdigheid en door Bean beheerde gelijktijdigheid.

De annotatie javax.ejb.ConcurrencyManagement definieert het gelijktijdigheidsbeleid voor een methode. Standaard gebruikt de EJB-container door containers beheerde gelijktijdigheid.

De @ConcurrencyManagement annotatie duurt een javax.ejb.ConcurrencyManagementType waarde. De mogelijkheden zijn:

  • ConcurrencyManagementType.CONTAINER voor door containers beheerde gelijktijdigheid.
  • ConcurrencyManagementType.BEAN voor door bonen beheerde gelijktijdigheid.

5.1. Door containers beheerde gelijktijdigheid

Simpel gezegd, in door containers beheerde gelijktijdigheid, bepaalt de container hoe de toegang van klanten tot methoden.

Laten we de @ConcurrencyManagement annotatie met waarde javax.ejb.ConcurrencyManagementType.CONTAINER:

@Singleton @Startup @ConcurrencyManagement (ConcurrencyManagementType.CONTAINER) openbare klasse CountryStateContainerManagedBean implementeert CountryState {...}

Om het toegangsniveau voor elk van de bedrijfsmethoden van de singleton te specificeren, gebruiken we javax.ejb.Lock annotatie. javax.ejb.LockType bevat de waarden voor de @Slot annotatie. javax.ejb.LockType definieert twee waarden:

  • LockType.WRITE - Deze waarde biedt een exclusieve vergrendeling voor de aanroepende client en voorkomt dat alle andere clients toegang hebben tot alle methoden van de bean. Gebruik dit voor methoden die de status van de singleton bean wijzigen.
  • LockType.READDeze waarde biedt gelijktijdige vergrendelingen aan meerdere clients om toegang te krijgen tot een methode.

    Gebruik dit voor methoden die alleen gegevens van de bean lezen.

Met dit in gedachten zullen we de setStates () methode met @Lock (LockType.WRITE) annotatie, om gelijktijdige update van de status door clients te voorkomen.

Om klanten toe te staan ​​de gegevens gelijktijdig te lezen, zullen we aantekeningen maken getStates () met @Lock (LockType.READ):

@Singleton @Startup @ConcurrencyManagement (ConcurrencyManagementType.CONTAINER) openbare klasse CountryStateContainerManagedBean implementeert CountryState {private final Map

Om de uitvoering van de methoden voor een lange tijd te stoppen en de andere clients voor onbepaalde tijd te blokkeren, gebruiken we de javax.ejb.AccessTimeout annotatie voor time-out van lang wachtende oproepen.

Gebruik de @AccessTimeout annotatie om het aantal milliseconden time-out van de methode te definiëren. Na de time-out gooit de container een javax.ejb.ConcurrentAccessTimeoutException en de uitvoering van de methode wordt beëindigd.

5.2. Door bonen beheerde gelijktijdigheid

In Bean managed concurrency heeft de container geen controle over gelijktijdige toegang tot Singleton Session Bean door clients. De ontwikkelaar moet zelf concurrency implementeren.

Tenzij concurrency wordt geïmplementeerd door de ontwikkelaar, zijn alle methoden tegelijkertijd toegankelijk voor alle clients. Java biedt het synchronisatie en vluchtig primitieven voor het implementeren van gelijktijdigheid.

Lees voor meer informatie over gelijktijdigheid java.util.concurrent hier en Atomic Variables hier.

Laten we voor door bonen beheerde gelijktijdigheid de @ConcurrencyManagement annotatie met de javax.ejb.ConcurrencyManagementType.BEAN waarde voor de Singleton Session Bean-klasse:

@Singleton @Startup @ConcurrencyManagement (ConcurrencyManagementType.BEAN) openbare klasse CountryStateBeanManagedBean implementeert CountryState {...}

Vervolgens schrijven we het setStates () methode die de toestand van de boon verandert met gesynchroniseerd trefwoord:

openbare gesynchroniseerde ongeldige setStates (String-land, Lijststaten) {countryStatesMap.put (land, staten); }

De gesynchroniseerd trefwoord maakt de methode toegankelijk voor slechts één thread tegelijk.

De getStates () methode verandert de toestand van de Boon niet en hoeft dus niet de gesynchroniseerd trefwoord.

6. Klant

Nu kunnen we de client schrijven om toegang te krijgen tot onze Singleton Session Bean.

We kunnen de Session Bean inzetten op applicatiecontainerservers zoals JBoss, Glassfish enz. Om het simpel te houden, zullen we de javax gebruiken.ejb.embedded.EJBContainer klasse. EJBContainer draait in dezelfde JVM als de client en biedt de meeste services van een enterprise bean-container.

Eerst maken we een instantie van EJBContainer. Deze containerinstantie zoekt en initialiseert alle EJB-modules die aanwezig zijn in het klassenpad:

openbare klasse CountryStateCacheBeanTest {privé EJBContainer ejbContainer = null; private context context = null; @Before public void init () {ejbContainer = EJBContainer.createEJBContainer (); context = ejbContainer.getContext (); }}

Vervolgens krijgen we het javax.naming.Context object uit het geïnitialiseerde containerobject. De ... gebruiken Context we kunnen bijvoorbeeld de verwijzing krijgen naar CountryStateContainerManagedBean en roep de methoden aan:

@Test openbare leegte whenCallGetStatesFromContainerManagedBean_ReturnsStatesForCountry () gooit uitzondering {String [] verwachtStates = {"Texas", "Alabama", "Alaska", "Arizona", "Arkansas"}; CountryState countryStateBean = (CountryState) context .lookup ("java: global / singleton-ejb-bean / CountryStateContainerManagedBean"); Lijst actualStates = countryStateBean.getStates ("Verenigde Staten"); assertNotNull (actualStates); assertArrayEquals (verwachteStates, actualStates.toArray ()); } @Test public void whenCallSetStatesFromContainerManagedBean_SetsStatesForCountry () gooit uitzondering {String [] verwachtStates = {"California", "Florida", "Hawaii", "Pennsylvania", "Michigan"}; CountryState countryStateBean = (CountryState) context .lookup ("java: global / singleton-ejb-bean / CountryStateContainerManagedBean"); countryStateBean.setStates ("UnitedStates", Arrays.asList (verwachteStates)); Lijst actualStates = countryStateBean.getStates ("Verenigde Staten"); assertNotNull (actualStates); assertArrayEquals (verwachteStates, actualStates.toArray ()); }

Evenzo kunnen we de Context instantie om de referentie voor Bean-Managed Singleton Bean te krijgen en de respectieve methoden aan te roepen:

@Test public void whenCallGetStatesFromBeanManagedBean_ReturnsStatesForCountry () gooit uitzondering {String [] verwachtStates = {"Texas", "Alabama", "Alaska", "Arizona", "Arkansas"}; CountryState countryStateBean = (CountryState) context .lookup ("java: global / singleton-ejb-bean / CountryStateBeanManagedBean"); Lijst actualStates = countryStateBean.getStates ("Verenigde Staten"); assertNotNull (actualStates); assertArrayEquals (verwachteStates, actualStates.toArray ()); } @Test public void whenCallSetStatesFromBeanManagedBean_SetsStatesForCountry () gooit uitzondering {String [] verwachtStates = {"California", "Florida", "Hawaii", "Pennsylvania", "Michigan"}; CountryState countryStateBean = (CountryState) context .lookup ("java: global / singleton-ejb-bean / CountryStateBeanManagedBean"); countryStateBean.setStates ("UnitedStates", Arrays.asList (verwachteStates)); Lijst actualStates = countryStateBean.getStates ("Verenigde Staten"); assertNotNull (actualStates); assertArrayEquals (verwachteStates, actualStates.toArray ()); }

Beëindig onze tests door het EJBContainer in de dichtbij() methode:

@After public void close () {if (ejbContainer! = Null) {ejbContainer.close (); }}

7. Conclusie

Singleton Session Beans zijn net zo flexibel en krachtig als elke standaard Session Bean, maar stellen ons in staat om een ​​Singleton-patroon toe te passen om de status te delen met de klanten van onze applicatie.

Gelijktijdigheidsbeheer van de Singleton Bean kan eenvoudig worden geïmplementeerd met behulp van Container-Managed Concurrency, waarbij de container zorgt voor gelijktijdige toegang door meerdere klanten, of u kunt ook uw eigen aangepaste gelijktijdigheidsbeheer implementeren met Bean-Managed Concurrency.

De broncode van deze tutorial is te vinden op GitHub.