Enterprise Java Asynchronous EJB v141202 Asynchronous EJB 1 Goals Enterprise Java • Be able to produce messages from the EJB server • Be able to consume messages within the EJB server • Be able to define timers within EJB server v141202 Asynchronous EJB 2 Objectives • • • • Enterprise Java EJB JMS Producers EJB JMS Consumers (Message Driven Beans; MDBs) Asynchronous Methods EJB Timers v141202 v111128 Asynchronous EJB Asynchronous EJB 33 EJB JMS Producers Enterprise Java • Obtain Resources – ConnectionFactory – Destination • Create Session – integrate with JTA transaction • Publish Message v141202 v111128 Asynchronous EJB Asynchronous EJB 44 Obtaining a ConnectionFactory Enterprise Java • Using annotations @Stateless public class SellerEJB ... { @Resource(lookup=“java:/JmsXA") private ConnectionFactory connFactory; – Lookup points to global JNDI name – benefits • concise & simple – drawbacks • mixes deployment concepts with Java code v141202 v111128 Asynchronous EJB Asynchronous EJB 55 Enterprise Java Obtaining a ConnectionFactory (cont.) • Using ejb-jar.xml <ejb-name>SellerEJB</ejb-name> <resource-ref> <res-ref-name>jms/ConnectionFactory</res-ref-name> <res-type>javax.jms.ConnectionFactory</res-type> <lookup-name>java:/JmsXA</lookup-name> <injection-target> <injection-target-class> ejava.examples.asyncmarket.ejb.SellerEJB </injection-target-class> <injection-target-name> connFactory </injection-target-name> </injection-target> </resource-ref> ... – Lookup moved away from code to DD – factory injected into EJB – ejb-jar.xml is no longer vendor/deployment-neutral v141202 v111128 Asynchronous EJB Asynchronous EJB 66 Enterprise Java Obtaining a ConnectionFactory (cont.) • Using jboss-ejb3.xml <session> <ejb-name>SellerEJB</ejb-name> <resource-ref> <res-ref-name>jms/ConnectionFactory</res-ref-name> <jndi-name>java:/JmsXA</jndi-name> </resource-ref> </session> – Lookup is now removed • replaced with vendor/deployment specific reference – Required 3 files to complete v141202 v111128 Asynchronous EJB Asynchronous EJB 77 Obtaining a Destination Enterprise Java • Using annotations @Stateless public class SellerEJB implements SellerLocal, SellerRemote { ... @Resource(lookup="java:/topic/ejava/examples/asyncMarket/topic1", type=Topic.class) private Destination sellTopic; – Lookup points to global JNDI entry – benefits • concise and simple – drawbacks • mixes deployment properties with implementation v141202 v111128 Asynchronous EJB Asynchronous EJB 88 Obtaining a Destination (cont.) Enterprise Java • Using ejb-jar.xml <resource-env-ref> <resource-env-ref-name>jms/sellTopic</resource-env-ref-name> <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type> <lookup-name>topic/ejava/examples/asyncMarket/topic</lookup-name> <injection-target> <injection-target-class> ejava.examples.asyncmarket.ejb.SellerEJB </injection-target-class> <injection-target-name>sellTopic</injection-target-name> </injection-target> </resource-env-ref> – mappedName moved away from Java and to DD – note resource-env-ref used for Destinations v141202 v111128 Asynchronous EJB Asynchronous EJB 99 Getting a Session Enterprise Java @TransactionAttribute(TransactionAttributeType.REQUIRED) public long sellProduct(String sellerId, AuctionItem item) throws MarketException { Connection connection = null; Session session = null; try { connection = connFactory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); ... } catch (JMSException ex) { log.error("error publishing sell", ex); ctx.setRollbackOnly(); throw new EJBException("error publishing sell:" + ex); } finally { try { if (session != null) { session.close(); } if (connection != null) { connection.close(); } } catch (JMSException ex) { log.error("unable to close resources", ex); } } } v141202 v111128 Asynchronous EJB Asynchronous EJB 1010 Enterprise Java Integrating JMS into the Transaction Person seller = sellerDAO.getPersonByUserId(sellerId); seller.getItems().add(item); item.setOwner(seller); auctionItemDAO.createItem(item); publishForSale(session, item); return item.getId(); v141202 v111128 Asynchronous EJB Asynchronous EJB 1111 Publishing the Message Enterprise Java protected void publishForSale(Session session, AuctionItem item) throws JMSException { MessageProducer producer = null; try { producer = session.createProducer(sellTopic); MapMessage message = session.createMapMessage(); message.setJMSType("forSale"); message.setLong("id", item.getId()); message.setString("name", item.getName()); message.setString("seller", item.getOwner().getUserId()); message.setLong("startDate", item.getStartDate().getTime()); message.setLong("endDate", item.getEndDate().getTime()); message.setDouble("minBid", item.getMinBid()); message.setDouble("bids", item.getBids().size()); message.setDouble("highestBid", (item.getHighestBid() == null ? 0.00 : item.getHighestBid().getAmount())); producer.send(message); } finally { if (producer != null) { producer.close(); } } } v141202 v111128 Asynchronous EJB Asynchronous EJB 1212 EJB JMS Consumers; MDBs Enterprise Java • “Message Driven Bean” • Introduced in EJB 2.0 to support JMS providers • Extended in EJB 2.1 to support non-JMS message providers – using the Java EE Connector API • commonly called JCA • EJB 3.0 added @Annotation support for configuration • Java EE Providers – must support a JMS provider – must support external providers through JCA • Session and Entity Beans cannot be a MessageListener – can poll for messages with MessageConsumer.receive() – can be wrapped by an MDB to be called asynchronously v141202 v111128 Asynchronous EJB Asynchronous EJB 1313 MessageDriven Bean Configuration • • • • • Enterprise Java Destination Type Destination Selector Message Acknowledgement ... v141202 v111128 Asynchronous EJB Asynchronous EJB 1414 MDB Configuration Enterprise Java • Using annotations @MessageDriven(name="BuyerMDB", activationConfig={ @ActivationConfigProperty( propertyName="destinationType", propertyValue="javax.jms.Topic"), @ActivationConfigProperty( propertyName="destination", propertyValue="topic/ejava/.../topic1"), @ActivationConfigProperty( propertyName="messageSelector", propertyValue= "JMSType in ('forSale', 'saleUpdate')"), @ActivationConfigProperty( propertyName="acknowledgeMode", propertyValue="Auto-acknowledge") }) public class BuyerMDB implements MessageListener { v141202 v111128 Asynchronous EJB Asynchronous EJB 1515 MDB Configuration (cont.) Enterprise Java • Using ejb-jar.xml <message-driven> <ejb-name>BuyerMDB</ejb-name> <ejb-class>ejava.examples.asyncmarket.ejb.BuyerMDB</ejb-class> <message-destination-type> javax.jms.Topic </message-destination-type> <activation-config> <activation-config-property> <activation-config-property-name> ... </activation-config-property-name> <activation-config-property-value> ... </activation-config-property-value> </activation-config-property> </activation-config> v141202 v111128 Asynchronous EJB Asynchronous EJB 1616 MDB Configuration (cont.) Enterprise Java • Using jboss.xml <message-driven> <ejb-name>BuyerMDB</ejb-name> <destination-jndi-name> topic/ejava/examples/asyncMarket/topic1 </destination-jndi-name> v141202 v111128 Asynchronous EJB Asynchronous EJB 1717 MDB Structure Enterprise Java @MessageDriven(name="BuyerMDB", activationConfig={ ... }) public class BuyerMDB implements MessageListener { @PostConstruct public void init() { ... } public void onMessage(Message message) { try { log.debug("onMessage:" + message.getJMSMessageID()); MapMessage auctionMsg = (MapMessage)message; long itemId = auctionMsg.getLong("id"); processAuctionItem(itemId); } catch (Exception ex) { log.error("error processing message", ex); } } v141202 Asynchronous EJB 18 MDB and Transactions Enterprise Java • SUPPORTED – message receipt/acknowledgement integrated with overall transaction • NOT_SUPPORTED – message receipt/acknowledgement independent of transactions within processing v141202 v111128 Asynchronous EJB Asynchronous EJB 1919 Asynchronous Methods Enterprise Java • Scenario – Task(s) may take considerable time to complete – Client need not wait for them to complete • @javax.ejb.Asynchronous – Return control to the client before EJB is invoked – Any session bean business method may be made @Asynchronous* (*see Serialization note below) • null return type – Client and issued task fully decoupled from one another • java.util.concurrent.Future return type – Allows task and client to coordinate a future return value – Client returns instance of javax.ejb.AsyncResult – Not Serializable (i.e., cannot use directly with RMI client) v141202 Asynchronous EJB 20 Synchronous Example: Client EJB Enterprise Java @Stateless public class AuctionMgmtEJB implements AuctionMgmtRemote, AuctionMgmtLocal { private @EJB AuctionMgmtActionEJB actions; public void workSync(int count, long delay) { DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS"); long startTime = System.currentTimeMillis(); for (int i=0; i<count; i++) { log.info(String.format("%s issuing sync request, delay=%d", df.format(new Date()), delay)); Date date= actions.doWorkSync(delay); log.info(String.format("sync waitTime=%d msecs", System.currentTimeMillis()-startTime)); } long syncTime = System.currentTimeMillis() - startTime; log.info(String.format("workSync time=%d msecs", syncTime)); } v141202 Asynchronous EJB 21 Synchronous Example: Helper EJB Enterprise Java @Stateless public class AuctionMgmtActionEJB { public Date doWorkSync(long delay) { DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS"); log.debug(String.format("sync method %d starting %d delay at %s", Thread.currentThread().getId(), delay, df.format(new Date()))); try { Thread.sleep(delay); } catch (Exception ex) { ... } Date now = new Date(); log.debug(String.format("sync method %d completed %d delay at %s", Thread.currentThread().getId(), delay, df.format(now))); return now; } v141202 Asynchronous EJB 22 Synchronous Example: Results Enterprise Java 11:06:44,624 INFO [AuctionMgmtEJB:306] 11:06:44.612 issuing sync request, delay=3000 11:06:44,626 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:44.626 11:06:47,628 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:47,630 INFO [AuctionMgmtEJB:309] sync waitTime=3018 msecs 11:06:47,631 INFO [AuctionMgmtEJB:306] 11:06:47.631 issuing sync request, delay=3000 11:06:47,634 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:47.634 11:06:50,636 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:50,637 INFO [AuctionMgmtEJB:309] sync waitTime=6025 msecs 11:06:50,637 INFO [AuctionMgmtEJB:306] 11:06:50.637 issuing sync request, delay=3000 11:06:50,638 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:50.638 11:06:53,640 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:53.640 11:06:53,641 INFO [AuctionMgmtEJB:309] sync waitTime=9029 msecs 11:06:53,642 INFO [AuctionMgmtEJB:312] workSync time=9030 msecs v141202 Asynchronous EJB 23 Asynchronous Example: Client EJB Enterprise Java @Stateless public class AuctionMgmtEJB implements AuctionMgmtRemote, AuctionMgmtLocal { public void workAsync(int count, long delay) { DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS"); long startTime = System.currentTimeMillis(); List<Future<Date>> results = new ArrayList<Future<Date>>(); for (int i=0; i<count; i++) { log.info(String.format("%s issuing async request, delay=%d", df.format(new Date()), delay)); Future<Date> date = actions.doWorkAsync(delay); results.add(date); log.info(String.format("async waitTime=%d msecs", System.currentTimeMillis()-startTime)); } for (Future<Date> f: results) { log.info(String.format("%s getting async response", df.format(new Date()))); try { Date date = f.get(); } catch (Exception ex) { throw new EJBException("unexpected error in future.get():"+ex);} log.info(String.format("%s got async response", df.format(new Date()))); } long asyncTime = System.currentTimeMillis() - startTime; log.info(String.format("workAsync time=%d msecs", asyncTime)); } v141202 Asynchronous EJB 24 Enterprise Java Asynchronous Example: Helper EJB @Stateless public class AuctionMgmtActionEJB { @javax.ejb.Asynchronous public java.util.concurrent.Future<Date> doWorkAsync(long delay) { DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS"); log.debug(String.format("async method %d starting %d delay at %s", Thread.currentThread().getId(), delay, df.format(new Date()))); try { Thread.sleep(delay); } catch (Exception ex) { ... } Date now = new Date(); log.debug(String.format("async method %d completed %d delay at %s", Thread.currentThread().getId(), delay, df.format(now))); return new javax.ejb.AsyncResult<Date>(now); } v141202 Asynchronous EJB 25 Asynchronous Example: Results Enterprise Java 11:06:53,650 INFO [AuctionMgmtEJB:325] 11:06:53.650 issuing async request, delay=3000 11:06:53,658 INFO [AuctionMgmtEJB:328] async waitTime=8 msecs 11:06:53,659 INFO [AuctionMgmtEJB:325] 11:06:53.659 issuing async request, delay=3000 11:06:53,659 DEBUG [AuctionMgmtActionEJB:41] async method 166 starting 3000 delay at 11:06:53.659 11:06:53,668 DEBUG [AuctionMgmtActionEJB:41] async method 167 starting 3000 delay at 11:06:53.668 11:06:53,668 INFO [AuctionMgmtEJB:328] async waitTime=18 msecs 11:06:53,669 INFO [AuctionMgmtEJB:325] 11:06:53.669 issuing async request, delay=3000 11:06:53,670 INFO [AuctionMgmtEJB:328] async waitTime=20 msecs 11:06:53,670 DEBUG [AuctionMgmtActionEJB:41] async method 168 starting 3000 delay at 11:06:53.670 11:06:53,671 INFO [AuctionMgmtEJB:331] 11:06:53.671 getting async response 11:06:56,667 DEBUG [AuctionMgmtActionEJB:47] async method 166 completed 3000 delay at 11:06:56,669 DEBUG [AuctionMgmtActionEJB:47] async method 167 completed 3000 delay at 11:06:56,669 INFO [AuctionMgmtEJB:339] 11:06:56.669 got async response 11:06:56,670 INFO [AuctionMgmtEJB:331] 11:06:56.670 getting async response 11:06:56,671 INFO [AuctionMgmtEJB:339] 11:06:56.671 got async response 11:06:56,672 DEBUG [AuctionMgmtActionEJB:47] async method 168 completed 3000 delay at 11:06:56,673 INFO [AuctionMgmtEJB:331] 11:06:56.672 getting async response 11:06:56,673 INFO [AuctionMgmtEJB:339] 11:06:56.673 got async response 11:06:56,674 INFO [AuctionMgmtEJB:342] workAsync time=3024 msecs v141202 Asynchronous EJB 26 EJB Timers Enterprise Java • Performs similar role of job schedulers – “cron” • Two types – Single-action • createTimer(Date expiration, Serializable info) – fires once at or after a specific time in the future • createTimer(long duration, Serializable info) – fires once after a specific delay period – Interval-timer • createTimer(Date intialExpiration, long intervalDuration, Serializable info) – continually fires every intervalDuration after the initialExpiration time • createTimer(long initialDuration, long intervalDuration, Serializable info) – continually fires every intervalDuration after the initialDuration delay v141202 v111128 Asynchronous EJB Asynchronous EJB 2727 EJB Timers Enterprise Java • Original EJB Timer Service part of EJB 2.1 • EJB 3.0 added annotation-based extensions that eliminated inheritance-based solution requirements • EJB 3.1 provided an overhaul of the overall service – Added declarative scheduling • @javax.ejb.ScheduleExpression • @javax.ejb.Schedule v141202 Asynchronous EJB 28 Declarative Calendar Timer Enterprise Java @Schedule(second="*/10", minute ="*", hour="*", dayOfMonth="*", month="*", year="*”, persistent=false) public void execute(Timer timer) { log.info("timer fired:" + timer); try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); } } v141202 Asynchronous EJB 29 Programmatic Calendar Timer Enterprise Java ScheduleExpression schedule = new ScheduleExpression(); schedule.second("*/10"); schedule.minute("*"); schedule.hour("*"); schedule.dayOfMonth("*"); schedule.month("*"); schedule.year("*"); auctionMgmt.initTimers(schedule); public void initTimers(ScheduleExpression schedule) { cancelTimers(); log.debug("initializing timers, schedule="+schedule); timerService.createCalendarTimer(schedule); } @Timeout public void execute(Timer timer) { log.info("timer fired:" + timer); try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); } } v141202 Asynchronous EJB 30 Programmatic Interval Timer Enterprise Java auctionMgmt.initTimers(10*1000); public void initTimers(long delay) { cancelTimers(); log.debug("initializing timers, checkItemInterval="+delay); timerService.createTimer(0,delay, "checkAuctionTimer"); } @Timeout public void execute(Timer timer) { log.info("timer fired:" + timer); try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); } } v141202 Asynchronous EJB 31 EJB Timers Enterprise Java • Accessing TimerService – Using Annotations @Resource private TimerService timerService; • Getting Timers timerService.getTimers() • Cancelling Timers for (Timer timer : (Collection<Timer>)timerService.getTimers()) { timer.cancel(); } • Timers – associated with the EJB that created them – are automatically integrated into JTA transaction v141202 Asynchronous EJB 32 EJB Timer Callbacks Enterprise Java • Using annotations public class AuctionMgmtEJB ... @Timeout public void execute(Timer timer) { try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); } } • Using interfaces public class AuctionMgmtEJB implements TimedObject, ... public void ejbTimeout(Timer timer) { ... } } v141202 Asynchronous EJB 33 javax.ejb.TimerService Enterprise Java public interface javax.ejb.TimerService{ javax.ejb.Timer createTimer(long, java.io.Serializable) throws ...; javax.ejb.Timer createSingleActionTimer(long, javax.ejb.TimerConfig) throws ...; javax.ejb.Timer createTimer(long, long, java.io.Serializable) throws ...; javax.ejb.Timer createIntervalTimer(long, long, javax.ejb.TimerConfig) throws ...; javax.ejb.Timer createTimer(java.util.Date, java.io.Serializable) throws ...; javax.ejb.Timer createSingleActionTimer(java.util.Date, javax.ejb.TimerConfig) throws ...; javax.ejb.Timer createTimer(java.util.Date, long, java.io.Serializable) throws ...; javax.ejb.Timer createIntervalTimer(java.util.Date, long,javax.ejb.TimerConfig) throws ...; javax.ejb.Timer createCalendarTimer(javax.ejb.ScheduleExpression) throws ...; javax.ejb.Timer createCalendarTimer(javax.ejb.ScheduleExpression, javax.ejb.TimerConfig) throws ...; java.util.Collection getTimers() throws ...; } v141202 Asynchronous EJB 34 Summary Enterprise Java • EJB JMS Publishers – integrates into Session and MDB processing • EJB JMS Subscriber – implemented using MDB – MDBs support more than JMS using JCA • Asynchronous Methods • EJB Timers – schedule re-activation of Session Bean – Single-action and interval v141202 v111128 Asynchronous EJB Asynchronous EJB 3535 References Enterprise Java • Java Messaging Service API – http://java.sun.com/javaee/5/docs/api/javax/jms/packagesummary.html • “Enterprise JavaBeans 3.0, 5th Edition”; Burke & Monsen-Haefel; ISBN 0-596-00978-X; O'Reilly v141202 Asynchronous EJB 36