Post on 28-Jun-2015
description
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.1
Graphic Section Divider
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.2
Easier Messaging with JMS 2.0Jagadish RamuApplication Server GroupOracle
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.3
The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.4
Java Message Service (JMS)
l A Java API for sending and receiving messagesl Many competing implementationsl Two distinct API variants
l Java SE applicationsl Java EE applications – adds additional features
l JTA (XA) transactionsl Replaces async MessageListener with MDBs, l Injection of connection factories and destinations using
@Resource l (New!) Injection and management of JMSContext objects
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.5
What JMS is and isn't
A standard APINot a messaging system in itselfNot a wire protocol
Defines Java API onlyDoesn't define API for non-Java clients (e.g. C++, HTTP) - but many implementations do
An application APINot (currently) an admin, management or monitoring API
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.6
JMS 2.0
JMS 1.1 (2002)Dozens of implementations, both standalone and as part of a full Java EE provider
JMS 2.0 (2013)Launched in 2011 as JSR 343Released in 2013 with Java EE 7Available in Open Message Queue 5.0 (standalone JMS provider) and in GlassFish 4.0 (full Java EE provider)Other implementations announced or in progress
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.7
What's new in JMS 2.0
l Simpler and easier to usel New messaging featuresl Better Java EE integration
l Define differences between JMS in SE and EE more clearly
l Simpler resource configurationl Standardized configuration of JMS MDBs
l Minor corrections and clarifications
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.8
JMS 2.0Simpler and easier to use
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.9
JMS API simplifications
l Make minor simplifications to existing standard API where it won't break compatibility
l Define new simplified API requiring fewer objectsJMSContext, JMSProducer, JMSConsumerIn Java EE, allow JMSContext to be injected and managed by the container
Twin-track strategy
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.10
Why did JMS 1.1 need simplifying?
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.11
JMS 1.1: Sending a message@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory; @Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessage(String payload) { try { Connection connection = connectionFactory.createConnection(); try { Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(demoQueue); TextMessage textMessage = session.createTextMessage(payload); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
13 lines of code just to send a message
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.12
JMS 1.1: Sending a message@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory; @Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessage(String payload) { try { Connection connection = connectionFactory.createConnection(); try { Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(demoQueue); TextMessage textMessage = session.createTextMessage(payload); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
must create several intermediate objects
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.13
JMS 1.1: Sending a message@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory; @Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessage(String payload) { try { Connection connection = connectionFactory.createConnection(); try { Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(demoQueue); TextMessage textMessage = session.createTextMessage(payload); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
redundant and misleading arguments
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.14
JMS 1.1: Sending a message@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory; @Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessage(String payload) { try { Connection connection = connectionFactory.createConnection(); try { Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(demoQueue); TextMessage textMessage = session.createTextMessage(payload); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
must close resourcesafter use!
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.15
JMS 1.1: Sending a message@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory; @Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessage(String payload) { try { Connection connection = connectionFactory.createConnection(); try { Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(demoQueue); TextMessage textMessage = session.createTextMessage(payload); messageProducer.send(textMessage); } finally { connection.close(); } } catch (JMSException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
all methods throw checked exceptions
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.16
Minor simplifications to the existing standard API
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.17
Minor simplifications to the standard API
Methods on javax.jms.Connection to create a Session:l JMS 1.1 method remains
connection.createSession(transacted, acknowledgeMode)l New method combines two parameters into one:
connection.createSession(sessionMode)l New method mainly for Java EE
connection.createSession()
Simpler API to create a Session
Session.SESSION_TRANSACTED Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE Session.DUPS_OK_ACKNOWLEDGE
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.18
Minor simplifications to the standard API
l Make JMS objects implement java.jang.AutoCloseableConnection Session MessageProducer MessageConsumer QueueBrowser
l Requires Java SE 7
Simpler API to close JMS objects
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.19
Minor simplifications to the standard JMS API
Make JMS objects implement java.jang.AutoCloseableConnection, Session, MessageProducer, MessageConsumer, QueueBrowser
Simpler API to close JMS objects
@Resource(lookup = "jms/connFactory")ConnectionFactory cf;
@Resource(lookup="jms/inboundQueue")Destination dest; public void sendMessage (String payload) throws JMSException { try ( Connection conn = connectionFactory.createConnection(); Session session = conn.createSession(); MessageProducer producer = session.createProducer(dest); ){ Message mess = sess.createTextMessage(payload); producer.send(mess); } catch(JMSException e){ // exception handling }}
close() is called automatically at end of block
Create closeable resources in a try-with-resources block
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.20
Completely new simplified API
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.21
Completely new simplified APIIntroducing JMSContext and JMSProducer
@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory;
@Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessageNew(String payload) { try (JMSContext context = connectionFactory.createContext();){ context.createProducer().send(demoQueue, payload); } catch (JMSRuntimeException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
13 linesreduced to 5
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.22
@Resource(lookup = "java:global/jms/demoConnectionFactory")ConnectionFactory connectionFactory;
@Resource(lookup = "java:global/jms/demoQueue")Queue demoQueue; public void sendMessageNew(String payload) { try (JMSContext context = connectionFactory.createContext();){ context.createProducer().send(demoQueue, payload); } catch (JMSRuntimeException ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); }}
Completely new simplified APIIntroducing JMSContext and JMSProducer
JMSContext combines Connection and Session
Payload can be sent directly
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.23
JMSContext (1/2)
l A new object which encapsulates a Connection, a Session and an anonymous MessageProducer
l Created from a ConnectionFactory
l Call close() after use, or create in a try-with-resources blockl Can also be injected (into a Java EE web or EJB application)
JMSContext context = connectionFactory.createContext(sessionMode);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.24
JMSContext (2/2)
l Can also create from an existing JMSContext (to reuse its connection – Java SE only)
l Used to create JMSProducer objects for sending messagesl Used to create JMSConsumer objects for receiving messagesl Methods on JMSContext, JMSProducer and JMSConsumer throw only
unchecked exceptions
JMSContext context2 = context1.createContext(sessionMode);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.25
JMSProducer
Messages are sent by creating a JMSProducer objectDoes not encapsulate a MessageProducer so is lightweightSupports method chaining for a fluid style
JMS 1.1
JMS 2.0
MessageProducer producer = session.createProducer();producer.send(destination,message);
JMSProducer producer = context.createProducer();producer.send(destination,message);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.26
JMSProducer
JMS 1.1
JMS 2.0
Setting message delivery options using method chaining
context.createProducer().setDeliveryMode(DeliveryMode.NON_PERSISTENT). setPriority(1).setTimeToLive(1000).send(destination,message);
MessageProducer producer = session.createProducer();producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);producer.setPriority(1);producer.setTimeToLive(1000);producer.send(destination,message);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.27
JMSProducer
JMS 1.1 (need to set on the message)
JMS 2.0 (can also set on the JMSProducer)
Setting message properties and headers
context.createProducer().setProperty("foo","bar").send(destination,"Hello");
MessageProducer producer = session.createProducer();TextMessage textMessage = session.createTextMessage("Hello);textMessage.setStringProperty("foo","bar");producer.send(destination,message);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.28
JMSProducer
Methods on JMSProducer to send a Messagesend(Destination dest, Message message)
No need to create a Messagesend(Destination dest, Map<String,Object> payload)send(Destination dest, Serializable payload)send(Destination dest, String payload)send(Destination dest, byte[] payload)
Use methods on JMSProducer to set delivery options, message headers and message properties
Sending message bodies directly
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.29
JMSConsumer
Messages are consumed by creating a JMSConsumer objectEncapsulates a MessageConsumerSimilar functionality and API to MessageConsumer
Synchronous
Asynchronous
Connection is automatically started (configurable)
JMSConsumer consumer = context.createConsumer(destination);Message message = consumer.receive(1000);
JMSConsumer consumer = context.createConsumer(destination);consumer.setMessageListener(messageListener);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.30
JMSConsumerReceiving message bodies directly
Methods on JMSConsumer that return a MessageMessage receive();Message receive(long timeout);Message receiveNoWait();
Methods on JMSConsumer that return message body directly<T> T receiveBody(Class<T> c);<T> T receiveBody(Class<T> c, long timeout);<T> T receiveBodyNoWait(Class<T> c);
When consuming messages synchronously
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.31
JMSConsumerReceiving message bodies directly
public String receiveMessage() throws NamingException { InitialContext initialContext = getInitialContext(); ConnectionFactory connectionFactory = (ConnectionFactory) initialContext.lookup("jms/connectionFactory"); Queue inboundQueue = (Queue)initialContext.lookup("jms/inboundQueue"); try (JMSContext context = connectionFactory.createContext();) { JMSConsumer consumer = context.createConsumer(inboundQueue); return consumer.receiveBody(String.class); } }
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.32
Extracting the body from a message
Old way
New way
In both the standard and simplified APIs
Message message = consumer.receive(1000);TextMessage textMessage = (TextMessage) message;String body = textMessage.getText();
Message message = consumer.receive(1000);String body = message.getBody(String.class);
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.33
Injection of JMSContext objectsinto a Java EE web or EJB container
@Inject @JMSConnectionFactory("jms/connectionFactory") private JMSContext context;
@Resource(mappedName = "jms/inboundQueue") private Queue inboundQueue;
public void sendMessage (String payload) { context.createProducer().send(inboundQueue, payload); }
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.34
Injection of JMSContext objects
Connection factory will default to platform default JMS
Specifying session mode
Specifying user and password (not for production use)
into a Java EE web or EJB container
@Inject private JMSContext context;
@Inject@JMSConnectionFactory("jms/connectionFactory")@JMSSessionMode(JMSContext.AUTO_ACKNOWLEDGE)private JMSContext context;
@Inject@JMSConnectionFactory("jms/connectionFactory")@JMSPasswordCredential(userName="admin",password="mypassword")private JMSContext context;
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.35
Injection of JMSContext objects
l Injected JMSContext objects have a scopeIn a JTA transaction, scope is the transactionIf no JTA transaction, scope is the request
l JMSContext is automatically closed when scope endsl Inject two JMSContext objects within the same scope and you get the
same objectIf @JMSConnectionFactory, @JMSPasswordCredential and
@JMSSessionMode annotations match Makes it easier to use same session within a transaction
into a Java EE web or EJB container
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.36
The four JMS APIs
Simplified API Standard API Legacy queue-specific API
Legacytopic-specific API
Introduced in
JMS 2.0 JMS 1.1 JMS 1.0 JMS 1.0
Main interfaces
ConnectionFactoryJMSContext JMSProducer JMSConsumer
ConnectionFactoryConnectionSessionMessageProducerMessageConsumer
QueueConnectionFactoryQueueConnectionQueueSession,QueueSender,QueueReceiver
TopicConnectionFactoryTopicConnectionTopicSessionTopicProducerTopicSubscriber
new and
simplified slightly
simplified deprecat
ed
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.37
New messaging features
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.38
Delivery delay
Allows a JMS client to schedule the future delivery of a messageNew method on MessageProducer
New method on JMSProducer
Sets minimum time in ms from that a message should be retained by the messaging system before delivery to a consumerWhy? If the business requires deferred processing, e.g. end of day
In both the standard and simplified APIs
public JMSProducer setDeliveryDelay(long deliveryDelay)
public void setDeliveryDelay(long deliveryDelay)
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.39
Async send
l Send a message and return immediately without blocking until an acknowledgement has been received from the server.
l Instead, when the acknowledgement is received, an asynchronous callback will be invoked
l New methods on MessageProducer
l Feature also available on JMSProducerl Why? Allows thread to do other work whilst waiting for the
acknowledgement
In both the standard and simplified APIs
messageProducer.send(message,completionListener)
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.40
Async send
Application specifies a CompletionListener instance
In both the standard and simplified APIs
public interface CompletionListener { void onCompletion(Message message); void onException(Message message, Exception exception);}
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.41
Better handling of "poison" messages
JMS 1.1 defines an optional JMS defined message property JMSXDeliveryCount.
When used, this is set by the JMS provider when a message is received, and is set to the number of times this message has been delivered (including the first time). JMS 2.0 will make this mandatory
Why? Allows app servers and applications to handle "poisonous" messages better
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.42
Multiple consumers on a topic subscription
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.43
How topics work in JMS 1.1
TopicProducer
Subscription
Consumer
Each message is copied to every subscription
In JMS 1.1, each subscription has a single consumer
Subscription may be persisted (durable) or memory-only (non-durable)
Subscription
Consumer
Subscription
Consumer
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.44
Shared subscriptions in JMS 2.0
TopicProducerConsumer
Shared Subscription
Each message is copied to every subscription A shared
subscription may have multiple consumers
Subscription may be persisted (durable) or memory-only (non-durable)
Unshared subscription
Consumer
Consumer
Consumer
Each message on the shared subscription is delivered to only one consumer
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.45
Easier definition of JMS resources in Java EE
(This is actually part of Java EE 7)
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.46
Easier definition of JMS resources in Java EE
l Java EE and JMS recommend applications should obtain JMS ConnectionFactory and Destination resources by lookup from JNDI
l Keeps application code portablel Creating these resources is a burden on the deployer, and is non-
standard
The problem
@Resource(lookupName = "jms/inboundQueue") private Queue inboundQueue;
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.47
Platform default connection factory
If you simply want to use the application server's built-in JMS provider, with no special settings:
Making the simple case simple
@Resource(lookup="java:comp/defaultJMSConnectionFactory")ConnectionFactory myJMScf;
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.48
Easier definition of JMS resources in Java EE
Application may specify the JMS connection factories and JMS destinations that it needs using annotations
New feature in Java EE 7
@JMSDestinationDefinition( name = "java:global/jms/myQueue", interfaceName = "javax.jms.Queue", destinationName = "demoQueue")
@JMSConnectionFactoryDefinition( name="java:global/jms/myCF")
JNDIname
JNDIname
queue/topic name
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.49
Easier definition of JMS resources in Java EE
Can specify additional standard or provider-specific properties
New feature in Java EE 7
@JMSDestinationDefinition( name = "java:global/jms/myQueue", interfaceName = "javax.jms.Queue", destinationName = "demoQueue")
@JMSConnectionFactoryDefinition( name="java:global/jms/myCF", maxPoolSize = 30, minPoolSize= 20, properties = { "addressList=mq://localhost:7676", "reconnectEnabled=true" })
standard properties
non-standard properties
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.50
Easier definition of JMS resources in Java EE
Multiple definitions of same type must be wrapped in collection annotations (due to restriction in how Java annotations work)
New feature in Java EE 7
@JMSDestinationDefinitions({ @JMSDestinationDefinition( name = "java:global/jms/myQueue1", interfaceName = "javax.jms.Queue", destinationName = "demoQueue1" ), @JMSDestinationDefinition( name = "java:global/jms/myQueue2", interfaceName = "javax.jms.Queue", destinationName = "demoQueue2" )})
@JMSConnectionFactoryDefinitions({ @JMSConnectionFactoryDefinition( name="java:global/jms/myCF1" ), @JMSConnectionFactoryDefinition( name="java:global/jms/myCF2" )})
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.51
Easier definition of JMS resources in Java EE
Alternatively application may specify the JMS connection factories and JMS destinations that it needs in the XML deployment descriptor
New feature in Java EE 7
<jms-destination> <name> java:global/jms/myQueue </name> <interface-name> javax.jms.Queue </interface-name> <destination-name> demoQueue </destination-name></jms-destination>
<jms-connection-factory> <name>java:global/jms/myCF</name> <max-pool-size>30</max-pool-size> <min-pool-size>20</min-pool-size> <property> <name>addressList</name> <value>mq://localhost:7676</value> </property> <property> <name>reconnectEnabled</name> <value>true</value> </property></jms-connection-factory>
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.52
Easier definition of JMS resources in Java EE
Resources configured in this way must be in one of the following namespaces:java:comp – may be used within same component onlyjava:module – may be used within same module onlyjava:app – may be used within same application onlyjava:global – may be used within any application
May be referenced just like any other resource (e.g. @Resource)
Available namespaces
@Resource(lookup="java:global/jms/myCF")ConnectionFactory myCF;
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.53
More standardized configuration of JMS MDBs
Joint effort with JSR 345 (EJB 3.2)
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.54
More standardized configuration of JMS MDBs
l Configuration of JMS MDBs is surprisingly non-standardl EJB 3.1 does not define how to specify
JNDI name of queue or topic (using annotation)JNDI name of connection factoryClientIDDurableSubscriptionName
l EJB 3.1 does not define how topic messages delivered to clustered MDBs
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.55
More standardized configuration of JMS MDBs
Can also be configured in ejb-jar.xml
New activation property to specify the queue or topic
@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "destinationLookup", propertyValue = "jms/myTopic"), . . .})
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.56
More standardized configuration of JMS MDBs
Can also be configured in ejb-jar.xml
New activation property to specify the connection factory
@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "connectionFactoryLookup", propertyValue = "jms/myCF"), . . .})
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.57
More standardized configuration of JMS MDBs
Surprisingly, these have never been standardized before
New activation properties to specify durable subscriptions
@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "subscriptionDurability", propertyValue = "Durable"), @ActivationConfigProperty( propertyName = "clientId", propertyValue = "myClientID"), @ActivationConfigProperty( propertyName = "subscriptionName", propertyValue = "MySub"), . . .})
clientId optional even for durable subscriptions
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.58
What’s new in JMS 2.0
l Simplicity and ease of usel New messaging featuresl Multi-threaded topic subscribers
Delivery delayAsync send
l Better Java EE integrationSimpler resource configurationStandardized configuration of JMS MDBs
l Minor corrections and clarifications
Summary
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.59
Try JMS 2.0
l JMS 2.0 in a standalone provider (for Java SE applications)Open Message Queue 5.0 mq.java.net/
l JMS 2.0 in a full Java EE 7 application serverGlassFish 4.0 glassfish.java.net/
l Try other implementations as they are releasedl See also jms-spec.java.net for lots of useful links
JMS 2.0, EJB 3.2 and Java EE 7
Copyright © 2013, Oracle and/or its affiliates. All rights reserved.60