This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
laboratoare:laborator09 [2014/12/11 13:04] Andrei Roșu-Cojocaru |
laboratoare:laborator09 [2015/01/12 12:57] (current) Andrei Roșu-Cojocaru |
||
|---|---|---|---|
| Line 408: | Line 408: | ||
| Unele funcţionalităţi permit unei aplicaţii să-şi îmbunătăţească performanţele. De exemplu, pentru mesajele se pot indica **timpi de expirare** după o anumită perioadă de timp, astfel încât consumatorii să nu primească informaţii care nu mai au actualitate. Totodată, mesajele pot fi transmise asincron, iar confirmarea de primire se poate realiza în mai multe moduri. Alte funcţionalităţi nu sunt legate de siguranţă, dar se pot dovedi utile în anumite circumstanţe. Astfel, se pot crea destinaţii temporare ce durează doar pe parcursul conexiunii în care au fost construite. | Unele funcţionalităţi permit unei aplicaţii să-şi îmbunătăţească performanţele. De exemplu, pentru mesajele se pot indica **timpi de expirare** după o anumită perioadă de timp, astfel încât consumatorii să nu primească informaţii care nu mai au actualitate. Totodată, mesajele pot fi transmise asincron, iar confirmarea de primire se poate realiza în mai multe moduri. Alte funcţionalităţi nu sunt legate de siguranţă, dar se pot dovedi utile în anumite circumstanţe. Astfel, se pot crea destinaţii temporare ce durează doar pe parcursul conexiunii în care au fost construite. | ||
| + | |||
| + | ===== Funcționalități Avansate JMS ===== | ||
| + | |||
| + | Prin intermediul funcționalităților avansate JMS, se asigură un nivel de încredere și performanțele solicitate de cele mai multe aplicații care folosesc un sistem de mesagerie: livrarea unui mesaj cu certitudine o singură dată (evitându-se astfel situațiile în care mesajul este pierdut sau este transmis de mai multe ori). Mecanismul prin care poate fi obținut un astfel de comportament este folosirea unui mesaj de tip ''PERSISTENT'' (implicit), care nu poate fi pierdut chiar în situația în care se produce o eroare la nivelul furnizorului de servicii de mesagerie (serverul de aplicații) și utilizarea unei tranzacții, unitate de lucru în care pot fi grupate mai multe operații (de tip trimitere / primire de mesaje) executate atomic (astfel încât fie toate sunt rulate cu succes, fie eșuează împreună). | ||
| + | |||
| + | Totodată, funcționalitățile avansate JMS permit îmbunătățirea performanței: | ||
| + | * specificarea unui timp de expirare al mesajelor face să nu fie transmise mesaje al căror conținut nu mai este de actualitate; | ||
| + | * transmiterea de mesaje în mod asincron; | ||
| + | * indicarea unor nivele pentru confirmarea transmiterii mesajelor; | ||
| + | * implementarea unor destinații temporare (care există doar în contextul conexiunii în care au fost create). | ||
| + | |||
| + | ==== Controlul procesului de confirmare a transmiterii mesajelor ==== | ||
| + | |||
| + | Un mesaj JMS nu este considerat ca fiind transmis cu succes până ce nu primește o confirmare explicită. Procesul de confirmare poate fi inițiat (ulterior livrării mesajului) fie de furnizorul de servicii de mesagerie, fie de componenta căreia i-a fost transmis, în funcție de mecanismul de confirmare utilizat. | ||
| + | |||
| + | **a)** În sesiunile tranzacționate local, un mesaj este confirmat atunci când sesiunea este consemnată (//eng.// committed). Dacă se produce o revenire la nivelul tranzacției, toate mesajele sunt transmise din nou. | ||
| + | |||
| + | **b)** Într-o tranzacție JTA (într-un container Java EE sau componentă Java Beans), un mesaj este confirmat atunci când tranzacția este consemnată. | ||
| + | |||
| + | **c)** În sesiune netranzacționate, confirmarea unui mesaj și momentul la care se produce acest lucru sunt controlate prin intermediul valorii pe care o ia parametrul metodei ''createContext()'': | ||
| + | * ''JMSContext.AUTO_ACKNOWLEGDE'' (implicită pentru clienți Java SE): contextul ''JMSContext'' confirmă în mod automat transmiterea mesajului, fie în momentul în care se termină metoda ''receive()'', fie când se termină metoda ''onMessage()'' a obiectului de tip ''MessageListener'' care a fost invocat pentru procesarea mesajului. | ||
| + | |||
| + | <note>O livrare sincronă a unui mesaj folosind un obiect ''JMSContext'' care folosește confirmarea automată implică faptul că trimiterea sa se realizează concomitent cu primirea confirmării, după care este realizată procesarea sa.</note> | ||
| + | |||
| + | * ''JMSContext.CLIENT_ACKNOWLEDGE'': clientul confirmă transmiterea prin apelul metodei ''acknowledge()'' pe obiectul mesaj; astfel, procesul de confirmare se realizează la nivelul sesiunii (confirmarea unui mesaj implică în mod automat confirmarea transmiterii tuturor mesajelor care au fost livrate în cadrul respectivei sesiuni, indiferent de obiectul pentru care este invocată metoda respectivă); | ||
| + | |||
| + | <note tip>Pentru platforma Java EE, o astfel de valoare poate fi folosită doar într-un client de tip aplicație, nu însă și pentru componente web sau de tip Java Bean.</note> | ||
| + | |||
| + | * ''JMSContext.DUPS_OK_ACKNOWLEDGE'': contextul ''JMSContext'' realizează procesul de confirmare cu întârziere, ceea ce poate determina transmiterea unei mesaje de mai multe ori, în cazul în care la nivelul furnizorului de servicii de mesageriese produc unele erori de funcționare; o astfel de opțiune poate fi folosită numai în cazul în care clientul poate gestiona situații de acest tip (atunci când un mesaj este retransmis, furnizorul de servicii de mesagerie va specifica acest lucru prin valoarea ''true'' a proprietății ''JMSRedelivered'' a antetului său). | ||
| + | |||
| + | <note>Acestă opțiune reduce supraîncărcarea de la nivelul sesiunii, eficientizând operațiile pe care aceasta trebuia să le realizeze pentru a preveni situația în care erau transmise duplicate ale unui mesaj.</note> | ||
| + | |||
| + | Dacă mesajele au fost preluate din cadrul unei cozi și nu au fost confirmate la momentul în care obiectul ''JMSContext'' este închis, furnizorul serviciului de mesagerie le reține, transmițându-le din nou când o componentă accesează destinația respectivă. Același comportament poate fi observat și în cazul unor abonamente durabile. Din acest motiv, atunci când se folosește o destinație de tip coadă sau un abonament durabil, trebuie utilizată metoda ''JMSContext.recover()'' pentru a se opri un obiect ''JMSContext'' netranzacționat, repornindu-l cu mesajele care nu au fost confirmate. Efectul acestei metode constă în plasarea indicatorului din cadrul fluxului de mesaje livrate spre a referi ultimul mesaj care a fost confirmat. Totuși, mesajele care sunt transmise pot fi diferite de cele care au fost transmise inițial, dacă acestea au expirat sau există mesaje cu prioritate mai mare. | ||
| + | |||
| + | În cazul unon abonamente tranzitive, furnizorul de servicii de mesagerie poate ignora mesajele care nu au fost confirmate atunci când este apelată metoda ''JMSContext.recover()''. | ||
| + | |||
| + | ==== Parametrii de configurare pentru transmiterea mesajelor ==== | ||
| + | |||
| + | În momentul în care este transmis un mesaj folosind sistemul Java Message Service, pot fi configurați diverși parametri, care indică: | ||
| + | * dacă acestea sunt persistente (dacă sunt pierdute sau nu în cazul în care se produce o eroare la nivelul furnizorului de servicii JMS); | ||
| + | * nivelul de prioritate, cu un impact asupra ordinii în care acestea sunt livrate; | ||
| + | * perioada de timp după care acestea expiră, implicând faptul că acestea nu vor mai fi livrate, în cazul în care nu mai sunt de actualitate; | ||
| + | * întârzierea cu care vor fi livrate. | ||
| + | |||
| + | <note tip>Înlănțuirea metodelor permite agregarea mai multor opțiuni în momentul în care este creat un producător de mesaje pentru care este apelată metoda ''send()''.</note> | ||
| + | |||
| + | === Specificarea persistenței mesajelor === | ||
| + | |||
| + | API-ul JMS implementează două mecanisme de livrare, indicând comportamentul pe care îl are furnizorul de servicii de mesagerie în cazul producerii unei erori. Aceste comportamente sunt definite prin intermediul unor câmpuri din interfața ''DeliveryMode'': | ||
| + | * ''PERSISTENT'' (implicit): indică faptul că nu este permisă pierderea mesajelor în cazul producerii unor erori, astfel că acestea sunt stocate (prin intermediul unor jurnale) pe un dispozitiv dedicat atunci când este transmis; | ||
| + | * ''NON_PERSISTENT'': nu oferă nici un fel de garanţie că mesajul nu este pierdut în situaţia în care furnizorul de servicii de mesagerie se defectează. | ||
| + | |||
| + | Pentru a indica modul de transmitere a mesajelor, se folosește metoda ''setDeliveryMode()'' din cadrul interfeței ''JMSProducer'', comportamentul respectiv aplicându-se pentru toate mesajele transmise de producătorul respectiv. | ||
| + | |||
| + | <code java> | ||
| + | jmsContext.createProducer().setDeliveryMode(DeliveryMode.NON_PERSISTENT).send(destination, message); | ||
| + | </code> | ||
| + | |||
| + | <note>În cazul în care nu se specifică nici un mecanism de livrare a mesajelor, se va considera că acesta este ''PERSISTENT''.</note> | ||
| + | |||
| + | <note tip>Folosirea modului de transmitere ''NON_PERSISTENT'' poate îmbunătăți performanțele, reducând supraîncărcarea spațiului de stocare, acesta putând fi utilizat numai în situația în care clientul poate opera și dacă se pierd unele mesaje.</note> | ||
| + | |||
| + | === Specificarea unor nivele de prioritate a mesajelor === | ||
| + | |||
| + | Utilizatorul poate indica nivele de prioritate a mesajelor pentru a furnizorul de servicii de mesagerie să livreze mesajele urgente mai repede. | ||
| + | |||
| + | În acest scop, trebuie folosită metoda ''setPriority()'' din cadrul interfeței ''JMSProducer'', nivelul de prioritate indicat (un număr întreg cuprins între 0 și 9) fiind folosit pentru toate mesajele transmise prin intermediul producătorului respectiv. | ||
| + | |||
| + | <code java> | ||
| + | jmsContext.createProducer().setPriority(Constants.MESSAGE_PRIORITY).send(destination, message); | ||
| + | </code> | ||
| + | |||
| + | <note>În cazul în care nu se specifică nici un nivel de prioritate, se va considera că acesta este 4.</note> | ||
| + | |||
| + | <note tip>Un furnizor de servicii de mesagerie care implementează specificația JMS va încerca să livreze mesajele cu prioritate mai mare înaintea mesajelor cu prioritate mai mică, însă nu există nici o garanție că mesajele sunt transmise exact în ordinea nivelului de prioritate specificat.</note> | ||
| + | |||
| + | === Specificarea unui timp de expirare a mesajelor === | ||
| + | |||
| + | Implicit, un mesaj nu expiră niciodată. | ||
| + | |||
| + | În situația în care conținutul unui mesaj nu mai este de actualitate după o anumită perioadă de timp, poate fi indicat un timp de expirare. Acest comportament poate fi folosit pentru mesajele a căror semnificație este strâns legată de momentul la care sunt transmise, pierzându-și valoarea după un anumit interval (mai mare sau mai mic). | ||
| + | |||
| + | În acest scop, trebuie folosită metoda ''setTimeToLive()'' din cadrul interfeței ''JMSProducer'', perioada de timp exprimată în milisecunde fiind folosită pentru toate mesajele transmise prin intermediul producătorului respectiv. | ||
| + | |||
| + | <code java> | ||
| + | jmsContext.createProducer().setTimeToLive(Constants.MESSAGE_EXPIRY_PERIOD).send(destination, message); | ||
| + | </code> | ||
| + | |||
| + | <note>Atunci când mesajul este trimis, atributul ''timeToLive'' este adăugat la momentul de timp curent pentru a se obține termenul la care acesta va expira. Orice mesaj care nu este livrat în această fereastră temporală este distrus, conservându-se astfel spațiul de stocare și resursele de procesare.</note> | ||
| + | |||
| + | <note tip>Dacă valoarea specificată pentru timpul de expirare este ''0'', mesajul nu va expira niciodată.</note> | ||
| + | |||
| + | === Specificarea întârzierii cu care vor fi livrate mesajele === | ||
| + | |||
| + | Dacă se dorește, se poate indica un interval de timp care trebuie să se scurgă până la momentul în care furnizorul de servicii de mesagerie livrează mesajele. | ||
| + | |||
| + | În acest scop trebuie folosită metoda ''setDeliveryDelay()'' din cadrul interfeței ''JMSProducer'', perioada de timp exprimată în milisecunde fiind folosită pentru toate mesajele transmise prin intermediul producătorului respectiv. | ||
| + | |||
| + | <code java> | ||
| + | jmsContext.createProducer().setDeliveryDelay(Constants.MESSAGE_DELIVERY_DELAY).send(destination, message); | ||
| + | </code> | ||
| + | |||
| + | ==== ==== | ||
| + | |||
| + | Întrucât metodele prin intermediul cărora sunt precizate valorile parametrilor de configurare a mesajelor întorc rezultate de tipul ''JMSProducer'', specificarea de valori pentru atribute diferite poate fi realizată în cadrul unei singure instrucțiuni, prin înlănțuire: | ||
| + | |||
| + | <code java> | ||
| + | jmsContext.createProducer() | ||
| + | .setDeliveryMode(DeliveryMode.NON_PERSISTENT) | ||
| + | .setPriority(Constants.MESSAGE_PRIORITY) | ||
| + | .setTimetoLive(Constants.MESSAGE_EXPIRY_PERIOD) | ||
| + | .setProperty("userAttribute", "userValue") | ||
| + | .setDeliveryDelay(Constants.MESSAGE_DELIVERY_DELAY) | ||
| + | .send(destination, message); | ||
| + | </code> | ||
| + | |||
| + | Așa cum se poate observa, interfața ''JMSProducer'' permite specificarea unor valori pentru atribute definite de utilizator. | ||
| + | |||
| + | Toți acești parametrii de configurare pot fi precizați pentru un obiect de tip mesaj, în cazul în care se dorește ca fiecare mesaj transmis de producător (prin metoda ''send()'') să fie caracterizat prin propriile valori ale acestora. | ||
| + | |||
| + | ==== Crearea unor destinații temporare ==== | ||
| + | |||
| + | De regulă, destinațiile (cozi și teme) sunt obiecte administrative care sunt create prin intermediul utilitarelor puse la dispoziție de furnizorul de servicii de mesagerie. Fiind persistente, acestea nu sunt instanțiate în cadrul aplicațiilor care le utilizează, ci obținute ca referințe prin injectarea în codul sursă. | ||
| + | |||
| + | API-ul JMS permite însă crearea de destinații temporare (obiecte de tipul ''TemporaryQueue'' și ''TemporaryTopic'') - în mod dinamic - care există numai pe durata conexiunii din care fac parte. | ||
| + | |||
| + | În acest scop, trebuie folosite metodele ''createTemporaryQueue()'', respectiv ''createTemporaryTopic()'' din cadrul obiectului ''JMSContext'': | ||
| + | |||
| + | <code java> | ||
| + | TemporaryQueue temporaryQueue = jmsContext.createTemporaryQueue(); | ||
| + | TemporaryTopic temporaryTopic = jmsContext.createTemporaryTopic(); | ||
| + | </code> | ||
| + | |||
| + | Orice producător de mesaje poate trimite mesaje către destinația temporară. | ||
| + | |||
| + | Un consumator de mesaje poate primi mesaje de la destinația temporară numai în cazul în care a fost obținut folosind aceeași conexiune care a creat-o. | ||
| + | |||
| + | <note warning>În cazul în care se închide conexiunea din care face parte o destinație temporară, aceasta este distrusă și conținutul său este pierdut definitiv.</note> | ||
| + | |||
| + | Mecanismele de tip cerere/răspuns reprezintă situații pentru care este adecvată implementarea unor destinații temporare: | ||
| + | * un producător de mesaje va trimite o cerere, indicând destinația temporară în atributul ''JMSReplyTo'' din cadrul antetului mesajului ca fiind locația la care trebuie primit răspunsul; | ||
| + | * un consumator de mesaje poate referi mesajul original, folosind atributul ''JMSCorrelationID'' din cadrul antetului mesajului ca având valoarea ''JMSMessageID'' a acestuia; | ||
| + | |||
| + | <code java> | ||
| + | TemporaryTopic temporaryTopic = jmsContext.createTemporaryTopic(); | ||
| + | Message requestMessage = jmsContext.createTextMessage("This is a request message"); | ||
| + | requestMessage.setJMSReplyTo(temporaryTopic); | ||
| + | jmsContext.createProducer().send(temporaryTopic, requestMessage); | ||
| + | </code> | ||
| + | |||
| + | <file java MessageAnalyzer.java> | ||
| + | public class MessageAnalyzer implements MessageListener { | ||
| + | |||
| + | @Override | ||
| + | public void onMessage(Message requestMessage) { | ||
| + | Message replyMessage = jmsContext.createTextMessage("This is a reply message"); | ||
| + | replyMessage.setJMSCorrelationID(requestMessage.getJMSMessageID()); | ||
| + | jmsContext.createProducer().send((Topic)requestMessage.getJMSReplyTo(), replyMessage); | ||
| + | } | ||
| + | | ||
| + | // ... | ||
| + | |||
| + | } | ||
| + | </file> | ||
| + | |||
| + | ==== Utilizarea de tranzacții locale JMS ==== | ||
| + | |||
| + | O tranzacție grupează o serie de operații într-o unitate de lucru atomică. | ||
| + | * în contextul în care o acțiune (dintre acestea) eșuează, tranzacția revine la starea anterioară, operațiile putând fi reluate din acest punct; | ||
| + | * dacă toate operațiile sunt executate cu succes, tranzacția poate fi consemnată. | ||
| + | |||
| + | Într-un client Java SE, tranzacțiile locale pot fi folosite pentru a grupa trimiteri și primiri de mesaje. | ||
| + | |||
| + | Metoda ''commit()'' a unui obiect de tip ''JMSContext'' va fi utilizată pentru a consemna o tranzacție. | ||
| + | * în cazul mai multor operații de trimitere a unor mesaje în cadrul aceleiași tranzacții, acestea nu vor fi transferate la nivelul destinației până când tranzacția nu este consemnată; | ||
| + | * în cazul mai multor operații de primire a unor mesaje în cadrul aceleiași tranzacții, ele nu vor fi confirmate până când tranzacția nu este consemnată. | ||
| + | |||
| + | Metoda ''rollback()'' a unui obiect de tip ''JMSContext'' va fi folosită pentru a se reveni la starea anterioară unei tranzacții. | ||
| + | * în cazul mai multor operații de trimitere a unor mesaje în cadrul aceleiași tranzacții, toate mesajele produse vor fi distruse; | ||
| + | * în cazul mai multor operații de primire a unor mesaje în cadrul aceleiași tranzacții, ele vor fi recuperate și livrate din nou (cu excepția cazului în care au expirat). | ||
| + | |||
| + | O sesiune tranzacționată este întotdeauna asociată unei tranzacții, aceasta putând fi obținută prin intermediul metodei ''createContext()'' invocată pe o fabrică de conexiuni, primind parametrul ''JMSContext.SESSION_TRANSACTED'': | ||
| + | |||
| + | <code java> | ||
| + | JMSContext jmsContext = connectionFactory.createContext(JMSContext.SESSION_TRANSACTED); | ||
| + | </code> | ||
| + | |||
| + | Odată cu apelarea uneia dintre metodele ''commit()'' sau ''rollback()'', se încheie tranzacția curentă. | ||
| + | |||
| + | Închiderea unei sesiuni tranzacționate implică revenirea la starea anterioară tranzacției în curs (inclusiv trimiteri / primiri de mesaje) - echivalentul unui apel al metodei ''rollback()''. | ||
| + | |||
| + | <note warning>În cadrul platformei Java EE (fie că este vorba de o aplicație web sau de o componentă Java Beans) nu pot fi folosite tranzacții locale, ci tranzacții JTA.</note> | ||
| + | |||
| + | În cadrul unei tranzacții locale, pot fi agregate mai multe operații de trimitere / primire mesaje, cu condiția ca acestea să fie realizate folosind același obiect de tip ''JMSContext''. | ||
| + | |||
| + | <note important>În implementarea unui mecanism de tip cerere / răspuns NU trebuie folosite tranzacții locale, întrucât o astfel de încercare va genera blocarea aplicației, de vreme ce metoda ''send()'' nu va fi realizată până ce nu se realizează consemnarea tranzacției. Totodată, metoda ''receive()'' nu poate primi mesajul atâta vreme cât acesta nu a fost trimis. | ||
| + | |||
| + | <code java> | ||
| + | // DEADLOCK !!! | ||
| + | Message messageToSend = jmsContext.createTextMessage(); | ||
| + | messageToSend.setJMSReplyTo(queue); | ||
| + | jmsContext.createProducer().send(queue, messageToSend); | ||
| + | consumer = jmsContext.createConsumer(queue); | ||
| + | Message messageReceived = consumer.receive(); | ||
| + | jmsContext.commit(); | ||
| + | </code> | ||
| + | </note> | ||
| + | |||
| + | Astfel, producerea și consumarea unui același mesaj nu pot fi incluse în cadrul aceleiași tranzacții. | ||
| + | |||
| + | Tranzacțiile implică operații care sunt realizate între clienți și furnizorul de servicii de mesagerie, care intervine între procesul de producere și de consumare de mesaje. O tranzacție reprezintă un contract între aceste entități, definind dacă un mesaj este transmis la o destinație sau este primit de la o destinație. O tranzacție NU poate implementa un contract între doi clienți (unul care trimite și unul care primește). | ||
| + | |||
| + | Trimiterea unuia sau mai multor mesaje de către un client către una sau mai multe destinații poate forma o tranzacție, de vreme ce implică o singură interacțiune cu furnizorul de servicii de mesagerie folosind un singur obiect de tip ''JMSContext''. | ||
| + | |||
| + | Primirea unuia sau mai multor mesaje de către un client de la una sau mai multe destinații poate forma o tranzacție, de vreme ce implică o singură interacțiune cu furnizorul de servicii de mesagerie folosind un singur obiect de tip ''JMSContext''. | ||
| + | |||
| + | Între doi clienți care nu interacționează direct (nu folosesc același obiect de tip ''JMSContext'') nu se poate realiza o tranzacție. | ||
| + | |||
| + | Aceasta este distincția dintre mesagerie și procesare sincronă. În loc de să realizeze o cuplare strânsă între destinatarul și expeditorul unui mesaj, JMS asociază expeditorul cu obiectul administrat și separat destinatarul cu obiectul administrat. Astfel, deși atât expeditorul cât și destinatarul sunt ambele strâns cuplate cu furnizorul de servicii de mesagerie, ele sunt slab cuplate între ele. | ||
| + | |||
| + | La crearea unui obiect de tip ''JMSContext'' poate fi specificat dacă sesiunea asociată este tranzacționată sau nu, prin includerea sau nu a parametrului ''JMSContext.SESSION_TRANSACTED'': | ||
| + | |||
| + | <code java> | ||
| + | try (JMSContext jmsContext = connectionFactory.createContext(JMSContext.SESSION_TRANSACTED)) { | ||
| + | // ... | ||
| + | } catch (Exception exception) { | ||
| + | System.out.println("An exception has occurred: " + exception.getMessage()); | ||
| + | if (Constants.DEBUG) | ||
| + | exception.printStackTrace(); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Metodele ''commit()'' și ''rollback()'' sunt asociate sesiunii din cadrul obiectului ''JMSContext''. Într-o singură tranzacție locală pot fi realizate mai multe cozi și teme sau pe orice combinație a acestora. | ||
| + | |||
| + | ==== Transmiterea asincronă de mesaje ==== | ||
| + | |||
| + | De regulă, atunci când se transmite un mesaj persistent, metoda ''send()'' se blochează până la momentul în care furnizorul de servicii de mesagerie confirmă faptul că mesajul a fost livrat cu succes. | ||
| + | |||
| + | Mecanismul de transmitere asincronă de mesaje permite ca aplicația să realizeze această operație, continuându-și funcționarea concomitent cu așteptarea rezultatului metodei ''send()''. | ||
| + | |||
| + | <note tip>O astfel de funcționalitate este disponibilă numai în cadrul clienților Java SE.</note> | ||
| + | |||
| + | Implementarea unui mecanism de transmitere asincronă a unor mesaje presupune furnizarea unui obiect cu apel invers (//eng.// callback) de tip ''CompletionListener'' pentru care trebuie specificată cel puțin metoda ''onCompletion()'', apelată în mod automat atunci când operația ''send()'' a fost realizată cu succes. | ||
| + | |||
| + | <note tip>Opțional, poate fi dezvoltată și metoda ''onException()'', care va fi invocată în mod automat dacă operația ''send()'' eșuează.</note> | ||
| + | |||
| + | <file java SendMethodListener.java> | ||
| + | public class SendMethodListener implements CompletionListener { | ||
| + | |||
| + | @Override | ||
| + | public void onCompletion(Message message) { | ||
| + | System.out.println("Message " + message + " sent successfully"); | ||
| + | } | ||
| + | | ||
| + | @Override | ||
| + | public void onException(Message message, Exception exception) { | ||
| + | System.out.println("Message " + message + " not sent successfully: " + exception.getMessage()); | ||
| + | if (Constants.DEBUG) | ||
| + | exception.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | </file> | ||
| + | |||
| + | O implementare a acestei clase trebuie furnizată ca parametru al metodei ''setAsync()'' apelată în producătorul de mesaje (obiect de tipul ''JMSProducer'') pentru care se va invoca metoda ''send()''. | ||
| + | |||
| + | <code java> | ||
| + | CompletionListener completionListener = new SendMethodListener(); | ||
| + | jmsContext.createProducer().setAsync(completionListener).send(destination, message); | ||
| + | </code> | ||
| ===== Activitate de Laborator ===== | ===== Activitate de Laborator ===== | ||
| Line 562: | Line 831: | ||
| {{ :laboratoare:laborator09:glassfish10.png?nolink&700 }} | {{ :laboratoare:laborator09:glassfish10.png?nolink&700 }} | ||
| - | ==== Server ==== | + | ==== Client ==== |
| În //Configurations// → //server-config// → //Java Message Service//: | În //Configurations// → //server-config// → //Java Message Service//: | ||
| Line 614: | Line 883: | ||
| **c)** În clasa ''ContactsList'', în metoda ''close()'', să se transmită mesajul ''Constants.LOGOUT_MESSAGE'' către toţi utilizatorii cu care s-a comunicat recent (din ''recentContactsListItem''). Se va apela metoda ''publish()'' a obiectului ''communicator'' având ca parametrii numele de utilizator al destinatarului şi mesajul propriu-zis. | **c)** În clasa ''ContactsList'', în metoda ''close()'', să se transmită mesajul ''Constants.LOGOUT_MESSAGE'' către toţi utilizatorii cu care s-a comunicat recent (din ''recentContactsListItem''). Se va apela metoda ''publish()'' a obiectului ''communicator'' având ca parametrii numele de utilizator al destinatarului şi mesajul propriu-zis. | ||
| - | **d)** În clasa ''ContactsList'', în metoda ''handleMessage()'', să se analizeze mesajul ''Constants.LOGOUT_MESSAGE'', parcurgându-se lista ''onnectedContactsListItem'' şi adăugându-se la aceasta numele de utilizator din mesajul primit, în cazul în care utilizatorul nu se regăseşte în aceasta. | + | **d)** În clasa ''ContactsList'', în metoda ''handleMessage()'', să se analizeze mesajul ''Constants.LOGOUT_MESSAGE'', parcurgându-se lista ''connectedContactsListItem'' şi eliminându-se din ea numele de utilizator din mesajul primit, în cazul în care utilizatorul face parte din aceasta. |
| **8.** [**10 puncte**] Să se modifice comportamentul aplicaţiei astfel încât utilizatorii să poată primi şi mesajele transmise în răstimpul în care nu au fost disponibili. | **8.** [**10 puncte**] Să se modifice comportamentul aplicaţiei astfel încât utilizatorii să poată primi şi mesajele transmise în răstimpul în care nu au fost disponibili. | ||
| Line 635: | Line 904: | ||
| ===== Soluții ===== | ===== Soluții ===== | ||
| - | disponibile pe 5 ianuarie 2015 | + | [[https://github.com/aipi2014/Laborator09/tree/master/EclipseEE/|Proiect EclipseEE]]\\ |
| + | [[https://github.com/aipi2014/Laborator09/tree/master/NetBeans/|Proiect NetBeans]] | ||