31
Lottery JMS, Message Driven Bean, JMX, Singleton Session Bean Óbudai Egyetem, Java Enterprise Edition Műszaki Informatika szak Labor 7 Bedők Dávid 2016.10.04. v0.9

Lottery - uni-obuda.hu

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Lottery - uni-obuda.hu

LotteryJMS, Message Driven Bean, JMX, Singleton Session Bean

Óbudai Egyetem, Java Enterprise EditionMűszaki Informatika szakLabor 7

Bedők Dávid2016.10.04.v0.9

Page 2: Lottery - uni-obuda.hu

JBoss management console● http://localhost:9990/● Létre kell hozni egy management user-t ehhez.

○ [JBOSS-HOME]/bin/add-user.[bat|sh]○ Management User (enter)○ Username: admin (are you sure? yes)○ Password: AlmafA11#○ Enter, Yes, Yes, Enter

● http://localhost:9990/ ○ Try Again és Login (BASIC AUTH)

2

> /jboss-eap-6.4/bin/add-user.sh admin AlmafA11#

Page 3: Lottery - uni-obuda.hu

Feladat

Készítsünk egy szolgáltatást, mely tárolja az ötös lottóhúzás eredményeit, ezek időpontját, nyereményalapját és a számot kihúzó személy nevét (legyen kinek megköszönni).A kihúzott számokat egy JMS queue interface-en* keresztül fogja a szolgáltatás megkapni (machine-to-machine interface).RESTful interface-en keresztül adjunk lehetőséget arra, hogy lekérdezzük az aktuális, illetve az összes sorsolás adatát.

* itt most interface alatt két rendszer közötti kommunikációs réteget értve

3

Page 4: Lottery - uni-obuda.hu

Kiegészítés

RESTful interface-en keresztül lehessen 5 szám megadásával “megkapni” a nyereményt!Minden sorsolásnak van egy nyereményalapja, melyet 100%-ban kiosztanak a nyertesek között.A szolgáltatás tartsa karban, hogy az aktuális “jogszabályok” szerint e nyereményalap mennyi százaléka jár az 1, 2, 3, 4 és 5 találatos játékosoknak.Megjegyzés: Mindez természetesen egyszerűsítés és nem is garantálja a helyes szétosztást, hiszen nem számol azzal, hogy pl. mennyi öt találatos szelvény volt.

A nyereményeloszlás százalékos paramétereit szabvány management felületen (JMX) lehessen konfigurálni!

4

Page 5: Lottery - uni-obuda.hu

Ismeretszerzés

● Java Message Service (JMS)○ JMS Queue○ JMS Topic (feladat nem érinti)○ Message Driven Bean (listener)○ JMS Client

● Java Management eXtension (JMX)○ Standard Management Bean (MBean)○ jconsole

● Singleton Session Bean○ speciális Statefull Session Bean

● EntityManager○ persist művelet○ JOIN FETCH 5

Page 6: Lottery - uni-obuda.hu

Project struktúra

lottery (root project)● lot-jmsclient (JMS client alkalmazás)● lot-ejbservice (EJB service réteg)● lot-persistence (persistence service réteg)● lot-webservice (RESTfull service réteg)Része az EAR-nak: sárga Standalone alkalmazás: kékA jms client alkalmazás classpathához jelenleg nem lesz szükség pl. egy lot-ejbserviceclient jar-ra, mivel a kommunikáció szabványos (nem kell pl. remote interface), és szöveges üzeneteket fogunk küldeni (nem kellenek stub-ok). Utóbbi megléte esetén megjelenne egy lot-ejbserviceclient jar is, mely része lenne mind az EAR mind a JMS client classpathának. 6

Page 7: Lottery - uni-obuda.hu

Adatbázis oldal

7

CREATE TABLE event (

event_id SERIAL NOT NULL,

event_puller CHARACTER VARYING(100) NOT NULL,

event_prizepool INTEGER NOT NULL,

event_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,

CONSTRAINT PK_EVENT_ID PRIMARY KEY (event_id)

);

CREATE TABLE drawnnumber (

drawnnumber_id SERIAL NOT NULL,

drawnnumber_event_id INTEGER NOT NULL,

drawnnumber_value INTEGER NOT NULL,

CONSTRAINT PK_DRAWNNUMBER_ID PRIMARY KEY (drawnnumber_id),

CONSTRAINT FK_DRAWNNUMBER_EVENT FOREIGN KEY (drawnnumber_event_id)

REFERENCES event (event_id) MATCH SIMPLE ON UPDATE RESTRICT ON DELETE RESTRICT

);

create-schema.sql

Page 8: Lottery - uni-obuda.hu

Legfrissebb és az összes sorsolásRESTful services

LotteryRestService● List<EventStub> getAllEvents() throws AdaptorException;

○ http://localhost:8080/lottery/api/service/event/list ● EventStub getLatestEvent() throws AdaptorException;

○ http://localhost:8080/lottery/api/service/event/latest

LotteryFacade● List<EventStub> getAllEvents() throws AdaptorException;● EventStub getLatestEvent() throws AdaptorException;

EventService● Event readLatest() throws PersistenceServiceException;

○ SELECT e FROM Event e JOIN FETCH e.numbers ORDER BY e.date DESC○ SetMaxResult(1)

● List<Event> readAll() throws PersistenceServiceException;○ SELECT e FROM Event e JOIN FETCH e.numbers ORDER BY e.prizePool

EventConverter● EventStub to(Event event);● List<EventStub> to(List<Event> events); 8

Page 9: Lottery - uni-obuda.hu

JOIN FETCH

9

SELECT

event0_.event_id AS event_id1_1_0_,

numbers1_.drawnnumber_id AS drawnnum1_0_1_,

event0_.event_date AS event_da2_1_0_,

event0_.event_prizepool AS event_pr3_1_0_,

event0_.event_puller AS event_pu4_1_0_,

numbers1_.drawnnumber_event_id AS drawnnum3_0_1_,

numbers1_.drawnnumber_value AS drawnnum2_0_1_,

numbers1_.drawnnumber_event_id AS drawnnum3_1_0__,

numbers1_.drawnnumber_id AS drawnnum1_0_0__

FROM

event event0_

INNER JOIN drawnnumber numbers1_ ON event0_.event_id=numbers1_.drawnnumber_event_id

ORDER BY

event0_.event_date DESC

server.log

A Set<DrawnNumber> LAZY módon van kapcsolva az Event-hez. A JOIN beköti a select-be a táblát (ez esetben a lekérdezés azonos lesz a bemutatottal), de nem attacholja az entitásokat (ha bejárjuk (pl. size()), akkor külön select-ben lekéri és attacholja. A JOIN FETCH e plusz lekérdezés nélkül attacholja is, és ez a hatékony megoldás ez esetben!

Page 10: Lottery - uni-obuda.hu

Új sorsolás adatainak rögzítése

10

Page 11: Lottery - uni-obuda.hu

Java Message Service (JMS)● Üzenetküldés alapú kommunikáció

○ “lazán” kötődő komponensek (beékelődik a kommunikációba az üzeneteket kezelő/tároló komponens)

● Message-Oriented Middleware (MOM)● JMS 1.1 (2002, JSR914, JEE6), JMS 2.0 (2013, JSR343, JEE7)● Típusai

○ point-to-point (queue)■ producer üzeneteket küld a queue-ba■ consumer üzenetet kiolvas a queue-ból■ egy üzenetet egy fogadó dolgoz fel (ack küldés is van)■ producer és consumer nem kell hogy egy időben “online” legyen

○ publish-subscribe (topic)■ publish üzeneteket küld a topic-ba■ subscriber(ek) megkapják a topic-ba küldött üzenetet■ egy üzenet több fogadó is feldolgoz(hat)■ publisher és subscriber között van időbeli függés, “tankönyvi”

eset szerint a komponensek egyszerre “online”-ok (de vannak speciális feliratkozások)

11

Page 12: Lottery - uni-obuda.hu

JBoss 6.4 - MOM, JMS provider

● HornetQ messaging○ http://hornetq.jboss.org/○ Deprecated, JBoss 7.x-től JBoss A-MQ váltja fel○ http://www.jboss.org/products/amq/overview/○ JMS 1.1 és JMS 2.0 támogatás

● Verzió: 2.3.25.Final (JBoss 6.4 esetén)

12

Page 13: Lottery - uni-obuda.hu

JMS Queue létrehozása

13

<?xml version="1.0" encoding="UTF-8"?>

<messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">

<hornetq-server>

<jms-destinations>

<jms-queue name="lotteryqueue">

<entry name="jms/queue/lotteryqueue" />

<entry name="java:jboss/exported/jms/queue/lotteryqueue" />

</jms-queue>

</jms-destinations>

</hornetq-server>

</messaging-deployment>

lotteryqueue-jms.xml

A file nevének *-jms.xml-nek kell lennie, és a deployments könyvtárba másolással “létrehozható”, de pl. standalone.xml-ben is lehet defininálni, illetve programozottan runtime is létrehozható.

local JNDI name

remote JNDI name

JNDI név “szabványok”:java:/jms/queue/lotteryqueue lesz a valós JNDI név.Remote JMS client a java:jboss/exported/ előtagot automatikusan fogja használni (JBoss JMS Client jar használata esetén)

Page 14: Lottery - uni-obuda.hu

Lottery ListenerMessage Driven Bean

14

package hu.qwaevisz.lottery.ejbservice.listener;

@MessageDriven(name = "LotteryListener", activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),

@ActivationConfigProperty(propertyName = "destination", propertyValue = "lotteryqueue"),

@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })

public class LotteryListener implements MessageListener {

@EJB

private LotteryFacade facade;

@PostConstruct

public void initialize() {

...

}

@Override

public void onMessage(final Message message) {

...

}

}

LotteryListener.java

Amikor a “lotteryqueue”-ba üzenet érkezik, a LotteryListener MDB aktiválódik, és az onMessage() metódusa meghívásra kerül a feldolgozandó üzenettel.Ha kivételt dob a metódus, az üzenet feldolgozás rollback-elődik, és nem kerül ki a sorból!

Page 15: Lottery - uni-obuda.hu

Lottery Listener - onMessage()Message Driven Bean

15

public void onMessage(final Message message) {

try {

final Queue destination = (Queue) message.getJMSDestination();

final String queueName = destination.getQueueName();

LOGGER.debug("New JMS message arrived into " + queueName + " queue (correlation id: " + message.getJMSCorrelationID() + ")");

if (message instanceof TextMessage) {

final TextMessage textMessage = (TextMessage) message;

String content = textMessage.getText();

[..] // parse content to int[] numbers

this.facade.createNewEvent(numbers);

} else {

LOGGER.error("...");

}

} catch (final JMSException | AdaptorException | NumberFormatException e) {

LOGGER.error(e, e);

}

}

LotteryListener.java

queue name: kinyerhető (hasznos, ha egy listener több queue-ra figyel egyidejűleg (erre van lehetőség)correlation id: tipikusan kliens hozza létre, és elküldi a JMS message-el, hogy később pl. egy async válasz során azonosítani tudja a “választ”.

● BytesMessage● MapMessage● ObjectMessage● StreamMessage● TextMessage

Page 16: Lottery - uni-obuda.hu

JMS Client ApplicationTávolról JMS üzenetet küld a lotteryqueue-ba, melyet az elindított JBoss EAS által indított HornetQ mint JMS MOM fog fogadni.

Ahhoz, hogy meg tudjuk szólítani ezt a szolgáltatást, az alábbiak szükségesek:

● JBoss initial context factory○ osztály neve: org.jboss.naming.remote.client.InitialContextFactory○ a classpath-on legyen elérhető ez az osztály

■ compile group: 'org.jboss.as', name: 'jboss-as-jms-client-bom', version: '7.2.0.Final'

● JBoss EAS host-ja (localhost) és remote portja (def: 4447)○ stanalone.xml | socket-binding-group | <socket-binding name="remoting" port="4447"/>

● JMS Connection Factory JNDI neve (jms/RemoteConnectionFactory)● Egy min. guest role-lal rendelkező user authentikációja (username és

password)● A cél queue JNDI neve (jms/queue/lotteryqueue)● Ha TextMessage helyett pl. ObjectMessage-et küldünk, akkor szükség

volna egy “serviceclient.jar”-ra, mely tartalmazza a Serializable DTO-kat (hasonlóan az ejb client-nél alkalmazottak szerint).

16

Page 17: Lottery - uni-obuda.hu

JMS user létrehozása

17

> \jboss-eap-6.4\bin\add-user.sh -a -u jmstestuser -p User#70365 -g guest

Page 18: Lottery - uni-obuda.hu

JMS Client ApplicationA csatlakozás lényegi részeit kiemelve

18

final Properties environment = new Properties();

environment.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");

environment.put(Context.PROVIDER_URL, "remote://localhost:4447");

environment.put(Context.SECURITY_PRINCIPAL, "jmstestuser");

environment.put(Context.SECURITY_CREDENTIALS, "User#70365");

final Context context = new InitialContext(environment);

final ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");

final Destination destination = (Destination) context.lookup("jms/queue/lotteryqueue");

Connection connection = connectionFactory.createConnection( "jmstestuser", "User#70365");

final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

final MessageProducer producer = session.createProducer(destination);

connection.start();

final TextMessage textMessage = session.createTextMessage( "1, 2, 3, 4, 5");

producer.send(textMessage);

connection.close();

SimpleClient.java

Page 19: Lottery - uni-obuda.hu

JMS (Remote) Connection FactoryA standalone-full.xml-ből előre konfigrálva kapjuk

19

<subsystem xmlns="urn:jboss:domain:messaging:1.4">

<jms-connection-factories>

<connection-factory name=" RemoteConnectionFactory">

<connectors>

<connector-ref connector-name="netty"/>

</connectors>

<entries>

<entry name="java:jboss/exported/ jms/RemoteConnectionFactory"/>

</entries>

</connection-factory>

</jms-connection-factories>

</subsystem>

standalone(-full).xml

A “java:jboss/exported” prefix-et a ClassPath-on lévő JBoss JMS Client jar “adja hozzá” a JNDI névhez.

Page 20: Lottery - uni-obuda.hu

Session Beans

Concurrency Management

CMC - Container-Managed Concurrency (def.)

BMC - Bean-Managed Concurrency

● Kizárólag Singleton Session Bean-ek esetén van értelmezve a Bean-Managed Concurrency!

● Utóbbi esetén engedélyezett pl. a synchronized és volatile kulcsszavak használata.

20

@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)

@ConcurrencyManagement(ConcurrencyManagementType.BEAN)

Page 21: Lottery - uni-obuda.hu

Singleton Session Bean

Az EJB container garantálja, hogy a Singleton Session Bean-ből ugyanazt a példányt fogja minden szálon használni.Természetesen nem arról van szó, hogy minden a SSB-t használó klienst szépen sorbaállít a container (ez bottleneck-je lenne az egész rendszernek).Vannak READ és vannak WRITE lock-kal rendelkező metódusai (kizólag CMC esetén használható).● READ: párhuzamosan több szálon is futhat (állapot

olvasás)● WRITE (def.): kizárólag egy szálon futhat (állapot

módosítás) 21

Page 22: Lottery - uni-obuda.hu

StateHolderSorsoló és a nyereményalap lekérdezése

22

package hu.qwaevisz.lottery.ejbservice.holder;@Singleton(mappedName = "ejb/lotteryState")@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)public class LotteryStateHolderImpl implements LotteryStateHolder {

private String puller;@PostConstructpublic void initialize() { this.puller = "Juanita A. Jenkins"; }

@Override@Lock(LockType.READ)public String getCurrentPuller() {

return this.puller;}@Override@Lock(LockType.WRITE)public void setCurrentPuller(String name) {

this.puller = name;}

}

StateHolderImpl.java

A prizePool tárolása és getter/setter üzleti metódusa mindezzel teljesen azonosan elkészíthető.A SSB-nek természetesen illendő interface-t készíteni (LotteryStateHolder ), mely jelen esetben a @Local annotációt megkapja.

Page 23: Lottery - uni-obuda.hu

LotteryFacade kiegészítéseLotteryListener hívja

23

package hu.qwaevisz.lottery.ejbservice.facade;@Stateless(mappedName = "ejb/lotteryFacade")public class LotteryFacadeImpl implements LotteryFacade {

@EJBprivate EventService eventService;@EJBprivate LotteryStateHolder stateHolder;

@Overridepublic void createNewEvent(int[] numbers) throws AdaptorException {

try {this.eventService.create(this.stateHolder.getCurrentPuller(),

this.stateHolder.getCurrentPrizePool(), numbers);} catch (final PersistenceServiceException e) {

LOGGER.error(e, e);throw new AdaptorException(e.getLocalizedMessage());

}}

}

LotteryFacadeImpl.java

Az EventService a persistence rétegben egy tranzakción belül be kell hogy insertálja az új event sort, illetve az 5 új drawnumber sort ehhez az eseményhez.

Page 24: Lottery - uni-obuda.hu

EventService kiegészítésePersistence réteg implementációja

24

package hu.qwaevisz.lottery.persistence.service;public class EventServiceImpl implements EventService {

@PersistenceContext(unitName = "lot-persistence-unit")private EntityManager entityManager;

@Overridepublic void create(String puller, Integer prizePool, int[] numbers)

throws PersistenceServiceException {try {

final Event event = new Event(puller, prizePool);for (final int number : numbers) {

event.addNumber(number);}this.entityManager.persist(event);

} catch (final Exception e) {throw new PersistenceServiceException("Unknown error when

fetching Events! " + e.getLocalizedMessage(), e);}

}}

EventServiceImpl.java

persist: egy új (vagy egy törlésre jelölt) entitás létrehozása, és egyben managed állapotba hozása

merge: egy detached (nem managed) entitás létrehozása (a metódus visszaadja a managed entitást, az átadott detached nem bántja)

public void addNumber(Integer number) { this.numbers.add(new DrawnNumber(number, this)); }

Fontos! Az Event entitás Set<DrawnNumber> numbers field-je @OneToMany annotációjában a cascade értéke CascadeType.ALL vagy PERSIST legyen!

Page 25: Lottery - uni-obuda.hu

Java Management eXtension

● A JMX technológia a JavaSE része, és természetesen a JEE is támogatja, szerver oldali komponensek monitorozására is használható.

● Managed Bean-ek létrehozása szükséges hozzá (MBean), melyeket az MBean server észlel és kezel.

● JMX klienst könnyedén írhatunk, de a szabvány csatorna lévén erre legtöbbször nincsen szükség (pl. jconsole egy Java SE-vel szállított kliens alkalmazás).

● Az MBean-eknek követniük kell a JMX specifikációban leírt szabályokat (JMX kliensek szabvány elérése ezáltal garantált).

● Simple Network Management Protocol (SNMP)

25

Page 26: Lottery - uni-obuda.hu

MBean készítésének szabályai

● Ha az implementáció Something class, akkor az interface SomethingMBean kell hogy legyen.

● Az MBean-ben műveleteket (operations) és attribútumokat (attributes) definiálhatunk.○ Read-only A típusú xyz attribútum esetén léteznie kell egy A getXyz()

metódusnak.○ Írható/olvasható A típusú xyz attribútum esetén létezni kell egy

A getXyz() és void setXyz( A ) metódusnak.○ Minden olyan metódus, mely nem getter illetve setter, automatikusan

műveletnek számít.

○ Nem lehet a getter/setter-t másra használni, nem lehet azonos névvel overload-olt gettert készíteni, nem lehet más az összetartozó getter/setter paraméterezése/visszatérési értékének típusa.

○ Egyszerű esetben a használható/javasolt típusok a java primitívek, tömbök, String-ek legyenek, de létezik komplexebb típus is (pl. TabularData). 26

Page 27: Lottery - uni-obuda.hu

LotteryMonitorJMX MBean készítése

27

package hu.qwaevisz.lottery.ejbservice.management;public class LotteryMonitor implements LotteryMonitorMBean {

@EJBprivate LotteryStateHolder stateHolder;@Overridepublic String getPuller() {

return this.stateHolder.getCurrentPuller();}@Overridepublic void setPuller(String name) {

this.stateHolder.setCurrentPuller(name);}public void start() throws Exception {

LOGGER.info("Start Lottery MBean");}public void stop() throws Exception {

LOGGER.info("Stop Lottery MBean");}

}

LotteryMonitor.java

A start() és a stop() metódusok a JMX MBean életciklusa során meghívódnak. Használatuk opcionális.

A getPrizePool() és setPrizePool() implementációja a puller alapján egyértelmű.

Page 28: Lottery - uni-obuda.hu

MBean regisztrációjaEJBService project

28

<?xml version="1.0" encoding="UTF-8"?>

<server xmlns="urn:jboss:service:7.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="urn:jboss:service:7.0 jboss-service_7_0.xsd">

<mbean code=" hu.qwaevisz.lottery.ejbservice.management.LotteryMonitor" name="lottery.mbean:service=LotteryMonitorMBean">

</mbean>

</server>

src/main/resources/jboss-service.xml

A code értékénél az osztályt kell megadni, mely megfelel mindenben a JMX MBean szabványoknak!

lottery.mbean lesz a topológiában a helye, LotteryMonitorMBean pedig ezen belül az MBean neve

Ez egy JBoss specifikus állomány, neve kötelezően jboss-service.xml kell hogy legyen.

Page 29: Lottery - uni-obuda.hu

jconsole

[JRE HOME]/bin/jconsole.[bat|sh]DE: JBoss esetén a jconsole classpath-ához hozzá kell fűzni további osztályokat (pl. a “jboss-cli-client.jar”-t), ezért a[JBOSS HOME]/bin/jconsole.[bat|sh] parancsal indítsuk el (mely hivatkozik a [JRE HOME]-ban lévőre.

A JBoss AS látszódni fog a Local process-ek között (de ugyanezen klienssel Remote JVM-hez is tudunk csatlakozni).Megjegyzés: MAC OS-en előfordul(hat) hogy a JBoss nem találja meg a jconsole.sh futtatásakor a JRE HOME-ot, ilyenkor lefutattva a JBoss alatti jconsole.sh-t a CLASSPATH-t beállítjuk a terminálban, és elindítjuk ugyanebben a terminálban mi a JRE HOME alatti jconsole-t. 29

Page 30: Lottery - uni-obuda.hu

Nyeremény ellenőrzése és kiszámolása

Új ismeretet nem tartalmazó üzleti metódus implementációja, mely során a kliens RESTful interface-en keresztül beküld 5 számot, és az alkalmazás ellenőrzi hogy az aktuális sorsolás során e számok “jók-e”, avagy sem, és a nyereményalapot és a nyeremények aktuális “jogszbályban” definiált eloszlási százalékuk ismeretében visszaadja a nyeremény összegét.A nyereményalap adatbázisból olvasható ki, míg az eloszlási százalékok MBean-en keresztül írhatóak/olvashatóak (MBean operations).

30

Page 31: Lottery - uni-obuda.hu

Gradle - Deploy to JBoss

31

ext {deployLocation = '/jboss-eap-6.4/standalone/deployments/'

}

task deployClean ( type: Delete ) {delete deployLocation + "${project.name}-${version}.ear"sleep(2000)

}

task deployEar ( type: Copy ) {dependsOn 'deployClean'from "build/libs/${project.name}-${version}.ear"

into deployLocation}

gradle.build

gradle clean build deployEar