The JNDI Environment Naming Context (ENC)
is central to the J2EE platform. The JNDI ENC specifies that JMS
connection factories (Topic-ConnectionFactory
and
QueueConnectionFactory
) can be bound within a JNDI
namespace and made available to any J2EE component at runtime. This
allows any J2EE component to become a JMS client.
For example, the
Wholesaler
JMS client developed in Chapter 4, could be modeled as a J2EE application
client, which would allow it to access a JMS connection factory
through the JNDI ENC:
public class Wholesaler implements javax.jms.MessageListener{ public Wholesaler(String username, String password){ try{ InitialContext jndiEnc = new InitialContext( ); TopicConnectionFactory factory = (TopicConnectionFactory)jndiEnc.lookup("java:comp/env/jms/broker");
connect = factory.createTopicConnection (username, password); session = connect.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); hotDealsTopic=(Topic)jndiEnc.lookup("java:comp/env/jms/HotDeals");
publisher = session.createPublisher(hotDealsTopic); ... } catch (javax.jms.JMSException jmse){ jmse.printStackTrace( ); System.exit(1); } catch (javax.naming.NamingException jne){ jne.printStackTrace( ); System.exit(1); } } ...
Notice that the
InitialContext
did not need a set of vendor specific
properties and that the lookup(
)
operations referenced a special namespace,
"java:comp/env/jms/
", to access the connection
factories. The JNDI ENC allows the J2EE component to remain ignorant
of the actual network location of the administered objects, and even
of the vendor that implements them. This allows the J2EE components
to be portable across JMS providers as well as J2EE platforms. In
addition, the JNDI names used to locate objects are logical bindings,
so the topics or queues bound to these names can change independently
of the actual bindings used by the JMS provider.
In the
XML
deployment descriptor for the Wholesaler
application client, the component developer declares that a JMS
connection factory and destination need to be bound within the JNDI
ENC:
<application-client> <display-name>Wholesaler Applicaton</display-name> <resource-ref> <description>Hot Deals Broker</description> <res-ref-name>jms/broker
</res-ref-name> <res-type>javax.jms.TopicConnectionFactory
</res-type> <res-auth>Container</res-auth> </resource-ref> ... <resource-env-ref> <description>Hot Deals Topic</description> <resource-env-ref-name>jms/HotDeals
</resource-env-ref-name> <resource-env-ref-type>javax.jms.Topic
</resource-env-ref-type> </resource-env-ref> ...
When the component is deployed, the J2EE vendor tools generate code to translate the JNDI ENC resource references into JMS-administered objects. This translation is done when the bean is deployed using administration tools.
Any J2EE component can access JMS connection factories and
destinations using the JNDI ENC. As an example, the
Wholesaler
client can be rewritten as a stateless session bean that uses the
JNDI ENC to obtain a JMS connection factory and destination:
public class WholesalerBean implements javax.ejb.SessionBean{ ... public void setSessionContext(SessionContext cntx){ try {InitialContext jndiEnc = new InitialContext( );
TopicConnectionFactory factory = (TopicConnectionFactory)
jndiEnc.lookup("java:comp/env/jms/broker");
connect = factory.createTopicConnection (username, password); session = connect.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);hotDealsTopic=(Topic)
jndiEnc.lookup("java:comp/env/jms/HotDeals");
publisher = session.createPublisher(hotDealsTopic); ... } ... } public void publishPriceQuotes(String dealDesc, String username, String itemDesc, float oldPrice, float newPrice){ try { javax.jms.StreamMessage message = session.createStreamMessage( ); message.writeString(dealDesc); ... publisher.publish( message, javax.jms.DeliveryMode.PERSISTENT, javax.jms.Message.DEFAULT_PRIORITY, 1800000); } catch ( javax.jms.JMSException jmse ){ jmse.printStackTrace( ); } } ... }
Although session,
entity, and web components can all act as JMS
producers, these components can only consume JMS messages
synchronously using the
MessageConsumer.receive(
)
methods. Calling one of the receive(
)
methods causes the JMS client to pole the queue or topic
and wait for a message.[1] These methods are used to
consume messages synchronously, while
MessageListener
objects are used to consume
messages asynchronously.
Only the message-driven bean and application client components can both produce and consume asynchronous messages. The web, session, and entity components cannot act as asynchronous JMS consumers because they are driven by synchronous request-reply protocols, not asynchronous messages. Web components respond to HTTP requests while entity and session beans respond to Java RMI-IIOP requests.
The fact that neither web components nor session and entity beans can asynchronously consume JMS messages was one of the things that led to development of the message-driven bean. The message-driven bean provides J2EE developers with a server-side JMS consumer that can consume asynchronous messages, something they didn't have before EJB 2.0.
[1] It's recommended that
the component developer use the nonblocking method,
receiveNoWait( )
, to conserve resources and avoid
blocking. Unrestricted blocking is not limited to any length of time,
and is therefore risky.