EnterpriseJava
Asynchronous EJB
v131202 Asynchronous EJB 1
EnterpriseJavaGoals
• 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
v131202 Asynchronous EJB 2
EnterpriseJavaObjectives
• EJB JMS Producers• EJB JMS Consumers (Message Driven Beans; MDBs)• Asynchronous Methods• EJB Timers
v131202 Asynchronous EJB 3v111128 Asynchronous EJB 3
EnterpriseJavaEJB JMS Producers
• Obtain Resources– ConnectionFactory– Destination
• Create Session– integrate with JTA transaction
• Publish Message
v131202 Asynchronous EJB 4v111128 Asynchronous EJB 4
EnterpriseJavaObtaining a ConnectionFactory
• Using annotations@Statelesspublic class SellerEJB ... { @Resource(mappedName=“java:/JmsXA") private ConnectionFactory connFactory;
–mappedName points to global JNDI name
–benefits• concise & simple
–drawbacks• mixes deployment concepts with Java code
v131202 Asynchronous EJB 5v111128 Asynchronous EJB 5
EnterpriseJavaObtaining 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> <mapped-name>java:/JmsXA</mapped-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>...
–mappedName moved away from code to DD–factory injected into EJB–ejb-jar.xml is no longer vendor/deployment-neutral
v131202 Asynchronous EJB 6v111128 Asynchronous EJB 6
EnterpriseJavaObtaining 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>
–mappedName is now removed• replaced with vendor/deployment specific reference
–Required 3 files to complete
v131202 Asynchronous EJB 7v111128 Asynchronous EJB 7
EnterpriseJavaObtaining a Destination
• Using annotations@Statelesspublic class SellerEJB implements SellerLocal, SellerRemote { ... @Resource(mappedName="java:/topic/ejava/examples/asyncMarket/topic1", type=Topic.class)
private Destination sellTopic;
–mappedName points to global JNDI entry–benefits
• concise and simple–drawbacks
• mixes deployment properties with implementation
v131202 Asynchronous EJB 8v111128 Asynchronous EJB 8
EnterpriseJavaObtaining a Destination (cont.)
• 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> <mapped-name>topic/ejava/examples/asyncMarket/topic</mapped-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
v131202 Asynchronous EJB 9v111128 Asynchronous EJB 9
EnterpriseJava
@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); } } }
Getting a Session
v131202 Asynchronous EJB 10v111128 Asynchronous EJB 10
EnterpriseJava
Person seller = sellerDAO.getPersonByUserId(sellerId); seller.getItems().add(item); item.setOwner(seller); auctionItemDAO.createItem(item); publishForSale(session, item); return item.getId();
Integrating JMS into the Transaction
v131202 Asynchronous EJB 11v111128 Asynchronous EJB 11
EnterpriseJava
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(); } } }
Publishing the Message
v131202 Asynchronous EJB 12v111128 Asynchronous EJB 12
EnterpriseJava
• “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
EJB JMS Consumers; MDBs
v131202 Asynchronous EJB 13v111128 Asynchronous EJB 13
EnterpriseJava
• Destination Type• Destination• Selector• Message Acknowledgement• ...
MessageDriven Bean Configuration
v131202 Asynchronous EJB 14v111128 Asynchronous EJB 14
EnterpriseJava
• 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 {
MDB Configuration
v131202 Asynchronous EJB 15v111128 Asynchronous EJB 15
EnterpriseJava
• 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>
MDB Configuration (cont.)
v131202 Asynchronous EJB 16v111128 Asynchronous EJB 16
EnterpriseJava
• Using jboss.xml<message-driven> <ejb-name>BuyerMDB</ejb-name> <destination-jndi-name> topic/ejava/examples/asyncMarket/topic1 </destination-jndi-name>
MDB Configuration (cont.)
v131202 Asynchronous EJB 17v111128 Asynchronous EJB 17
EnterpriseJava
v131202 Asynchronous EJB 18
MDB Structure@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); } }
EnterpriseJava
• SUPPORTED– message receipt/acknowledgement integrated with
overall transaction• NOT_SUPPORTED
– message receipt/acknowledgement independent of transactions within processing
MDB and Transactions
v131202 Asynchronous EJB 19v111128 Asynchronous EJB 19
EnterpriseJava
• 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)
Asynchronous Methods
v131202 Asynchronous EJB 20
EnterpriseJava
@Statelesspublic 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)); }
Synchronous Example: Client EJB
v131202 Asynchronous EJB 21
EnterpriseJava
@Statelesspublic 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;}
Synchronous Example: Helper EJB
v131202 Asynchronous EJB 22
EnterpriseJava
11:06:44,624 INFO [AuctionMgmtEJB:306] 11:06:44.612 issuing sync request, delay=300011:06:44,626 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:44.62611:06:47,628 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:47,630 INFO [AuctionMgmtEJB:309] sync waitTime=3018 msecs11:06:47,631 INFO [AuctionMgmtEJB:306] 11:06:47.631 issuing sync request, delay=300011:06:47,634 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:47.63411:06:50,636 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:50,637 INFO [AuctionMgmtEJB:309] sync waitTime=6025 msecs11:06:50,637 INFO [AuctionMgmtEJB:306] 11:06:50.637 issuing sync request, delay=300011:06:50,638 DEBUG [AuctionMgmtActionEJB:24] sync method 163 starting 3000 delay at 11:06:50.63811:06:53,640 DEBUG [AuctionMgmtActionEJB:30] sync method 163 completed 3000 delay at 11:06:53.64011:06:53,641 INFO [AuctionMgmtEJB:309] sync waitTime=9029 msecs
11:06:53,642 INFO [AuctionMgmtEJB:312] workSync time=9030 msecs
Synchronous Example: Results
v131202 Asynchronous EJB 23
EnterpriseJava
@Statelesspublic 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));}
Asynchronous Example: Client EJB
v131202 Asynchronous EJB 24
EnterpriseJava
@Statelesspublic 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); }
Asynchronous Example: Helper EJB
v131202 Asynchronous EJB 25
EnterpriseJava
11:06:53,650 INFO [AuctionMgmtEJB:325] 11:06:53.650 issuing async request, delay=300011:06:53,658 INFO [AuctionMgmtEJB:328] async waitTime=8 msecs11:06:53,659 INFO [AuctionMgmtEJB:325] 11:06:53.659 issuing async request, delay=300011:06:53,659 DEBUG [AuctionMgmtActionEJB:41] async method 166 starting 3000 delay at 11:06:53.65911:06:53,668 DEBUG [AuctionMgmtActionEJB:41] async method 167 starting 3000 delay at 11:06:53.66811:06:53,668 INFO [AuctionMgmtEJB:328] async waitTime=18 msecs11:06:53,669 INFO [AuctionMgmtEJB:325] 11:06:53.669 issuing async request, delay=300011:06:53,670 INFO [AuctionMgmtEJB:328] async waitTime=20 msecs11:06:53,670 DEBUG [AuctionMgmtActionEJB:41] async method 168 starting 3000 delay at 11:06:53.67011:06:53,671 INFO [AuctionMgmtEJB:331] 11:06:53.671 getting async response11: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 response11:06:56,670 INFO [AuctionMgmtEJB:331] 11:06:56.670 getting async response11:06:56,671 INFO [AuctionMgmtEJB:339] 11:06:56.671 got async response11: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 response11:06:56,673 INFO [AuctionMgmtEJB:339] 11:06:56.673 got async response
11:06:56,674 INFO [AuctionMgmtEJB:342] workAsync time=3024 msecs
Asynchronous Example: Results
v131202 Asynchronous EJB 26
EnterpriseJava
• 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
EJB Timers
v131202 Asynchronous EJB 27v111128 Asynchronous EJB 27
EnterpriseJava
• 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
EJB Timers
v131202 Asynchronous EJB 28
EnterpriseJava
@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); } }
Declarative Calendar Timer
v131202 Asynchronous EJB 29
EnterpriseJava
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);}
@Timeoutpublic void execute(Timer timer) { log.info("timer fired:" + timer); try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); }}
Programmatic Calendar Timer
v131202 Asynchronous EJB 30
EnterpriseJava
auctionMgmt.initTimers(10*1000);
public void initTimers(long delay) { cancelTimers(); log.debug("initializing timers, checkItemInterval="+delay); timerService.createTimer(0,delay, "checkAuctionTimer");}
@Timeoutpublic void execute(Timer timer) { log.info("timer fired:" + timer); try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); }}
Programmatic Interval Timer
v131202 Asynchronous EJB 31
EnterpriseJava
v131202 Asynchronous EJB 32
EJB Timers
• Accessing TimerService– Using Annotations
@Resource private TimerService timerService;
• Getting TimerstimerService.getTimers()
• Cancelling Timersfor (Timer timer : (Collection<Timer>)timerService.getTimers()) {
timer.cancel();}
• Timers– associated with the EJB that created them– are automatically integrated into JTA transaction
EnterpriseJava
v131202 Asynchronous EJB 33
EJB Timer Callbacks
• Using annotations public class AuctionMgmtEJB ...
@Timeoutpublic void execute(Timer timer) { try { checkAuction(); } catch (Exception ex) { log.error("error checking auction", ex); }}
• Using interfacespublic class AuctionMgmtEJB implements TimedObject, ...
public void ejbTimeout(Timer timer) { ... }}
EnterpriseJava
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 ...;}
javax.ejb.TimerService
v131202 Asynchronous EJB 34
EnterpriseJava
• 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
Summary
v131202 Asynchronous EJB 35v111128 Asynchronous EJB 35
EnterpriseJava
v131202 Asynchronous EJB 36
References
• Java Messaging Service API– http://java.sun.com/javaee/5/docs/api/javax/jms/package-
summary.html• “Enterprise JavaBeans 3.0, 5th Edition”; Burke &
Monsen-Haefel; ISBN 0-596-00978-X; O'Reilly