Komponenty sterowane komunikatami 1. Usługa JMS asynchroniczność, model przesyłania komunikatów, 2. Przykład wysyłanie wiadomości, odbieranie wiadomości, komponent sterowany komunikatami 3. Komponenty MDB cykl życia, wiązanie komunikatów. 1
Usługa JMS Specyfkacja JEE zawiera obsługę komunikatów JMS (Java Messaging Service). Za implementację usługi odpowiedzialny jest producent kontenera EJB. W technologii EJB usługa JMS może być wykorzystywana przez wszystkie typy komponentów. Komunikaty mogą być odbierane (konsumowane) przez aplikacje Javy lub przez komponenty sterowane komunikatami. 2
Asynchroniczność Przesyłanie komunikatów jest asynchroniczne, co oznacza, że klient JMS nie musi wstrzymywać działania do czasu otrzymania odpowiedzi na wysłany przezeń komunikat. Komunikat jest wysyłany do kolejki lub tematu i może zostać odebrany przez jednego lub wielu odbiorców. Ze względu na brak ścisłego związania nadawcy i odbiorcy, propagacja komunikatu nie zawiera kontekstów bezpieczeństwa i transakcji, niemniej wysyłanie komunikatu może następować w ramach transakcji rozproszonej. 3
Modele przesyłania komunikatów publikacja subskrypcja (1:N) subskrybent temat subskrybent subskrybent nadawca potencjalny odbiorca kolejka potencjalny odbiorca punkt - punkt (1:1) potencjalny odbiorca 4
Przykład struktura systemu klient serwer (usługi wiadomości i kontener EJB) JmsClient_ Reservation Producer queue/titan-reservationqueue Reservation Processor Bean JmsClient_ Ticket Consumer queue/titan-ticketqueue 5
Wysyłanie wiadomości package com.titan.clients; import javax.jms.*; import javax.naming.*; import java.util.*; import com.titan.processpayment.*; import com.titan.access.dataaccess; public class JmsClient_ReservationProducer { public static void main(string[] args) throws Exception { Properties p = new Properties(); p.load(jmsclient_reservationproducer.class.getresourceasstream("jndi.properties")); System.getProperties().putAll(p); 6
Wysyłanie wiadomości Integer cruiseid = new Integer(args[0]); int count = new Integer(args[1]).intValue(); Context jndicontext = getinitialcontext(); DataAccess access = (DataAccess) jndicontext.lookup("dataaccessbean/remote"); access.initializedb(); try { access.makepaymentdbtable(); catch (Exception ignored) { ConnectionFactory factory = (ConnectionFactory) jndicontext.lookup("connectionfactory"); Queue reservationqueue = (Queue) jndicontext.lookup("queue/titan-reservationqueue"); Queue ticketqueue = (Queue) jndicontext.lookup("queue/titan-ticketqueue"); Connection connect = factory.createconnection(); Session session = connect.createsession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer sender = session. createproducer(reservationqueue); 7
Wysyłanie wiadomości for (int i = 0; i < count; i++) { MapMessage message = session.createmapmessage(); // Used in ReservationProcessor // to send Tickets back out message.setjmsreplyto(ticketqueue); message.setstringproperty("messageformat", "Version 3.4"); message.setint("cruiseid", cruiseid); // either Customer 1 or 2, all we've got in database message.setint("customerid", i % 2 + 1); message.setint("cabinid", i % 5 + 1); // cabiny 100-109 message.setdouble("price", (double) 1000 + i); 8
Wysyłanie wiadomości // the card expires in about 30 days Date expdate = new Date(System.currentTimeMillis() + 30 * 24 * 60 * 60 * 1000L); message.setstring("creditcardnum", "5549861006051975"); message.setlong("creditcardexpdate", expdate.gettime()); message.setstring("creditcardtype", CreditCardDO.MASTER_CARD); System.out.println("Sending reservation message #" + i); sender.send(message); connect.close(); public static Context getinitialcontext() throws javax.naming.namingexception { return new InitialContext(); 9
Interfejs MessageListener Odbieranie komunikatów z reguły jest wykonywane przez obiekty implementujące interfejs javax.jmx.messagelistener: package javax.jms; public interface MessageListener { public void onmessage(message message); 10
Odbieranie wiadomości package com.titan.clients; import java.util.properties; import javax.jms.* import javax.naming.* import com.titan.travelagent.ticketdo; public class JmsClient_TicketConsumer implements MessageListener { public static void main(string[] args) throws Exception { Properties p = new Properties(); p.load(jmsclient_ticketconsumer.class.getresourceasstream("jndi.properties")); System.getProperties().putAll(p); new JmsClient_TicketConsumer(); while (true) { Thread.sleep(10000); 11
Odbieranie wiadomości public JmsClient_TicketConsumer() throws Exception { Context jndicontext = getinitialcontext(); ConnectionFactory factory = (ConnectionFactory) jndicontext.lookup("connectionfactory"); Queue ticketqueue = (Queue) jndicontext.lookup("queue/titan-ticketqueue"); Connection connect = factory.createconnection(); Session session = connect.createsession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer receiver = session. createconsumer(ticketqueue); receiver.setmessagelistener(this); System.out.println("Listening for messages..."); connect.start(); 12
Odbieranie wiadomości public void onmessage(message message) { try { ObjectMessage objmsg = (ObjectMessage) message; TicketDO ticket = (TicketDO) objmsg.getobject(); System.out.println("********************************"); System.out.println(ticket); System.out.println("********************************"); catch (JMSException displayed) { displayed.printstacktrace(); public static Context getinitialcontext() throws javax.naming.namingexception { return new InitialContext(); 13
Komponent sterowany komunikatami package com.titan.reservationprocessor; import com.titan.domain.*; import com.titan.processpayment.*; import com.titan.travelagent.*; import java.util.date; import javax.annotation.*; import javax.ejb.*; import javax.jms.*; import javax.persistence.*; import javax.naming.*; @MessageDriven(activationConfig={ @ActivationConfigProperty(propertyName="destinationType", propertyvalue="javax.jms.queue"), @ActivationConfigProperty(propertyName="destination", propertyvalue="queue/titan-reservationqueue")) 14
Adnotacja @ActivationConfgProperty @MessageDriven(activationConfig={ @ActivationConfigProperty( propertyname="destinationtype", propertyvalue="javax.jms.queue"), @ActivationConfigProperty( propertyname="messageselector", propertyvalue="messageformat = 'Version 3.4'"), @ActivationConfigProperty( propertyname="acknowledgemode", propertyvalue="auto-acknowledge")) messageselector fltr komunikatów, Message message = session.createmapmessage( ); message.setstringproperty("messageformat","version 3.4");... sender.send(message); acknowledgemode tryb potwierdzania otrzymania komunikatu, subscriptiondurability trwałość subskrypcji, destinationtype model wiadomości. 15
Komponent sterowany komunikatami public class ReservationProcessorBean implements javax.jms.messagelistener { @PersistenceContext(unitName= "titan") private EntityManager em; @EJB private ProcessPaymentLocal process; @Resource(mappedName="java:/JmsXA") private ConnectionFactory connectionfactory; public void onmessage(message message) { System.out.println("Received Message"); try { MapMessage reservationmsg = (MapMessage)message; int customerpk = reservationmsg.getint("customerid"); int cruisepk = reservationmsg.getint("cruiseid"); int cabinpk = reservationmsg.getint("cabinid"); double price = reservationmsg.getdouble("price"); 16
Komponent sterowany komunikatami // odczyt danych karty kredytowej Date expirationdate = new Date(reservationMsg.getLong("CreditCardExpDate")); String cardnumber = reservationmsg.getstring("creditcardnum"); String cardtype = reservationmsg.getstring("creditcardtype"); CreditCardDO card = new CreditCardDO(cardNumber, expirationdate, cardtype); Customer customer = em.find(customer.class, customerpk); Cruise cruise = em.find(cruise.class, cruisepk); Cabin cabin = em.find(cabin.class, cabinpk); Reservation reservation = new Reservation(customer, cruise, cabin, price, new Date()); em.persist(reservation); process.bycredit(customer, card, price); TicketDO ticket = new TicketDO(customer, cruise, cabin, price); deliverticket(reservationmsg, ticket); catch(exception e) { throw new EJBException(e); 17
Komponent sterowany komunikatami public void deliverticket (MapMessage reservationmsg, TicketDO ticket) throws JMSException, NamingException{ Queue queue = (Queue)reservationMsg.getJMSReplyTo(); // Queue queue = (Queue) //new InitialContext().lookup ("queue/titan-ticketqueue"); Connection connect = connectionfactory.createconnection(); Session session = connect.createsession(true,0); MessageProducer sender = session.createproducer(queue); ObjectMessage message = session.createobjectmessage(); message.setobject(ticket); sender.send(message); connect.close(); 18
Cykl życia komponentu sterowanego komunikatami Class.newInstance() @PostConstruct Nie istnieje @PreDestroy W puli gotowych komponentów Metoda biznesowa 19
Wiązanie komunikatów Komunikaty mogą być kierowane do konkretnego komponentu w celu dalszej obsługi EJB TravelAgent MDB TicketDistribution Inne systemy Specyfkacja EJB nie defniuje konkretnej adnotacji służącej do wiązania komunikatów. W tym celu należy skorzystać z deskryptora wdrożenia. 20
Wiązanie komunikatów <ejb-jar> <enterprise-beans> <session> <ejb-name>com.titan.travelagent.travelagentbean</ejb-name> <message-destination-ref> <message-destination-ref-name> jms/tickettopic </message-destination-ref-name> <message-destination-type> javax.jms.topic </message-destination-type> <message-destination-usage>produces</message-destination-usage> <message-destination-link>distributor</message-destination-link> <injection-target> <injection-target-class>javax.jms.topic</injection-target-class> <injection-target-name>topic</injection-target-name> </injection-target> </message-destination-ref> </session> 21
Wiązanie komunikatów <message-driven> <ejb-name>ticketdistributorejb</ejb-name> <message-destination-link>distributor</message-destination-link> </message-driven> </enterprise-beans> <assembly-descriptor> <message-destination> <message-destination-name>distributor</message-destination-name> </message-destination> </assembly-descriptor> </ejb-jar> 22
Podsumowanie Asynchroniczne przesyłanie komunikatów stanowi uzupełnienie luki wytworzonej przez synchroniczne działanie takich usług jak RMI, JAXP- RPC, czy też komponentów sesyjnych. Należy jednak pamiętać, że JMS nie jest jedynym, dostępnym mechanizmem wymiany komunikatów. 23