|
Welcome to the Enterprise Java Technologies Tech Tips for March 22, 2005. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java 2 Platform, Enterprise Edition (J2EE).
This issue covers:
Using Message-Driven Beans with EJB 2.1
Java API for XML Registries (JAXR)
These tips were developed using the Java 2, Enterprise Edition, v 1.4 SDK. You can download the SDK at http://java.sun.com/j2ee/1.4/download.html.
This issue of the Tech Tips is written by Robert Eckstein, a staff member of java.sun.com.
See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
You can download the sample archive for these tips. Any use of this code and/or information below is subject to the license terms.
For more Java technology content, visit these sites:
java.sun.com - The latest Java platform releases, tutorials, and newsletters.
java.net - A web forum for collaborating and building solutions together.
java.com - The marketplace for Java technology, applications and services.
USING MESSAGE-DRIVEN BEANS WITH EJB 2.1
There are three types of Enterprise JavaBeans (EJB) technology components: session beans, entity beans, and message-driven beans (or MDBs). Session beans, which can be both stateful and stateless, often represent business processes. Stateful session beans are typically accessed by a single client at a time, however both stateful and stateless session beans are accessed in a synchronous manner, and both are non-persistent. By comparison, entity beans represent business data that is saved to persistent storage. MDBs allow J2EE applications to use the Java Message Service (JMS) API to process messages between a bean and a client asynchronously. MDBs are the newest form of EJB -- they were introduced with EJB 2.0. When introduced, MDBs allowed J2EE applications to use the Java Message Service (JMS) API to process messages between a bean and a client asynchronously.
MDBs are similar in many aspects to stateless session beans. The EJB specification gives some examples that highlight some of the characteristics of MDBs that are similar to stateless session beans:
- An message-driven bean's instances retain no data or conversational state for a specific client.
- All instances of a message-driven bean are equivalent, allowing the EJB container to assign a message to any message-driven bean instance.
- The container can pool these instances to allow streams of messages to be processed concurrently.
- A single message-driven bean can process messages from multiple clients.
The most obvious difference between the two types of beans is that an MDB receives asynchronous JMS messages instead of synchronous method invocations (such as events). Session beans and entity beans allow you to send JMS messages and to receive them synchronously, but not asynchronously. These asynchronous messages can be sent by any J2EE component -- an application client, another enterprise bean, or a web component. With EJB 2.0, MDBs were simple to implement. Because MDBs only react to JMS messages, there is no need for the client interface or home interface that you would expect in other types of enterprise beans. MDBs only require a single class that implements the javax.ejb.MessageDrivenBean interface, and the JMS interface javax.jms.MessageListener. Here is a simple example of an MDB:
import javax.ejb.*;
import javax.jms.*;
public class MyMessageBean implements MessageDrivenBean,
MessageListener
{
public void ejbCreate() throws CreateException {...}
public void ejbRemove() { ... }
public void setMessageDrivenContext(
MessageDrivenContext ctx) { ... }
public void onMessage(Message msg) { ... }
}
Client components do not locate MDBs and invoke methods directly on them. Instead, a client accesses an MDB through JMS by sending messages to the message destination for which the MDB class is the MessageListener. Here is an example, based on the MDB example in the J2EE 1.4 Tutorial. To send a message to an MDB, a client first locates the connection factory and queue:
connectionFactory =
(ConnectionFactory) jndiContext.lookup(
"java:comp/env/jms/MyConnectionFactory");
destination =
(Queue) jndiContext.lookup("java:comp/env/jms/QueueName");
Next, the client creates the queue connection, session, and sender:
connection = connectionFactory.createConnection();
session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
messageProducer = session.createProducer(destination);
message = session.createTextMessage();
Finally, the client sends messages to the queue:
for (int i = 0; i < NUM_MSGS; i++) {
message.setText("This is message " + (i + 1));
System.out.println("Sending message: " +
message.gettext());
messageProducer.send(message);
}
When the queue receives a message, the EJB container invokes the onMessage() method of the MDB. This method contains the business logic that handles the processing of the message. It is the MDB's responsibility to parse the message and perform the necessary business logic. In the case of MyMessageBean, the onMessage() method could cast the incoming message to a TextMessage and direct the text to a log file:
public void onMessage(Message inMessage) {
TextMessage msg = null;
try {
if (inMessage instanceof TextMessage) {
msg = (TextMessage) inMessage;
logger.info("MESSAGE BEAN: Message received: " +
msg.getText());
} else {
logger.warning("Message of wrong type: " +
inMessage.getClass().getName());
}
} catch (JMSException e) {
e.printStackTrace();
} catch (Throwable te) {
te.printStackTrace();
}
}
What's New in EJB 2.1
With EJB 2.1, you are no longer tied to using JMS. This change is important for programmers who want to tie MDBs to web services. You can now have an MDB listen for other types of messages by implementing an appropriate interface. These interfaces are typically added through the use of a "connector" that conforms to the J2EE Connector Architecture. The J2EE Connector Architecture 1.5 defines a messaging contract specifically tailored to MDB. (See the November 23, 2004 Tech Tip J2EE Connector Architecture 1.5).
MDBs that are based on an asynchronous connector implement the standard javax.ejb.MessageDrivenBean interface, they also implement a specific messaging interface defined by the connector itself that is specific to the Enterprise Information System.
Let's look at a simple example involving a web services connector. The connector software resides in a Resource Archive (RAR) file. This RAR also defines a messaging interface and messaging API that a developer uses to create a web service MDB. Here is the hypothetical interface that the EJB 2.1 MDB must implement:
package com.someone.web;
public interface WebServiceListener {
public void receiveWebServiceMessage(
XMLDocument message);
}
In this case, the MDB will implement both the javax.ejb.MessageDrivenBean interface and the WebServiceListener interface. The following code shows an MDB that implements the WebServiceListener interface and processes XML messages:
package com.someone.web;
public class WebServiceBean implements
javax.ejb.MessageDrivenBean,
com.someone.web.WebServiceListener
{
MessageDrivenContext ejbContext;
public void setMessageDrivenContext(
MessgeDrivenContext context){
ejbContext = context;
}
public void ejbCreate()
{
// Code omitted
}
public void receiveWebServiceMessage(
XMLDocument message)
{
// Do something with message
}
public void ejbRemove(){
// code omitted
}
}
Note that the messaging interface used may declare a method that defines a return type. Again, the connector must come into play here, relaying the reply message back to the sender.
Deployment Descriptor Changes
There are also a number of deployment descriptor changes for MDBs in EJB 2.1. Because they no longer rely exclusively on JMS, the <message-selector> and <acknowledge-mode> elements have been replaced in EJB 2.1. In addition, a message-agnostic element, <activation-config>, was introduced to enumerate various properties of the MDB. These <activation-config> properties are passed to the connector when the message-driven bean is deployed.
Here is an example of some <activation-config> property settings:
<activation-config>
<activation-property>
<activation-config-property-name>messageSelector
</activation-config-property-name>
<activation-config-property-value>Priority = 'Urgent'
</activation-config-property-value>
<activation-property>
</activation-config>
EJB 2.1 defines the following four fixed properties for JMS-based MDBs, which supplant the older EJB 2.0 properties. They are:
acknowledgeMode
messageSelector
destinationType
subscriptionDurablity
EJB 2.1 also adds two new elements, <messaging-type> and <message-destination-type>, as follows:
<messaging-type>javax.jms.MessageListener
</messaging-type>
<message-destination-type>
javax.jms.Queue
</message-destination-type>
The <messaging-type> element indicates which message type will be used, it does this by stating the fully-qualified interface name. If no interface name is given, the container defaults to javax.jms.MessageListener. The <message-destination-type> element designates a fully-qualified class that represents the type of destination from which the bean will get messages. For JMS, the allowed values are javax.jms.Queue and javax.jms.Topic.
Message Linking
A message linking facility has been added to the deployment descriptors to specify MDB destinations to which any type of EJB can send messages. The deployment descriptor changes for the sending bean should look familiar to you if you use ejb-refs:
<session>
<ejb-name>SendingEJB</ejb-name>
<resource-ref>
<res-ref-name>jms/MyFactory</res-ref-name>
<res-type>javax.jms.MyConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<message-destination-ref>
<message-destination-ref-name>
jms/MyDestination
</message-destination-ref-name>
<message-destination-type>
javax.jms.Topic
</message-destination-type>
<message-destination-usage>
Produces
</message-destination-usage>
<message-destination-link>
MyDestinationLink
</message-destination-link>
</message-destination-ref>
</session>
The receiving MDB should have the following in its deployment descriptor information:
<message-driven>
<ejb-name>ReceivingEJB</ejb-name>
<messaging-type>
javax.jms.MessageListener
</messaging-type>
<transaction-type>Bean</transaction-type>
<message-destination-type>
javax.jms.Topic
</message-destination-type>
<message-destination-link>
MyDestinationLink
</message-destination-link>
</message-driven>
The following should appear in the assembly descriptor, to link the sending and receiving beans:
<assembly-descriptor>
<message-destination>
<message-destination-name>
MyDestinationLink
</message-destination-name>
</message-destination>
</assembly-descriptor>
For more information about message-driven beans, see the J2EE 1.4 Tutorial.
JAVA API FOR XML REGISTRIES (JAXR)
The Java API for XML Registries (JAXR) provides a convenient way to access standard business registries over the Internet. Business registries contain information that allows businesses to discover and use services provided by potential corporate partners. Typically, a business registers itself with a registry, or uses a registry to discover other businesses with which they might want to interact. This Tech Tip, which is based on a simple JAXR example provided in the Java Web Services Developer Pack 1.5 and also described in the J2EE 1.4 Tutorial, shows how the JAXR API can be used by a Java client application to access a business registry on the Internet.
A business registry is really a dedicated form of XML registry, it's a neutral third-party infrastructure that facilitates dynamic and loosely coupled business-to-business (B2B) interactions. These interactions typically follow one of two standards:
- The ebXML Registry and Repository standard, which is sponsored by the Organization for the Advancement of Structured Information Standards (OASIS) and the United Nations Centre for the Facilitation of Procedures and Practices in Administration, Commerce and Transport (U.N./CEFACT).
- The Universal Description, Discovery, and Integration (UDDI) project, which is being developed by a vendor consortium.
A registry provider is an entity that provides an implementation of a business registry that conforms to one of these standards.
JAXR Architecture
JAXR can be used on either side of a B2B interaction. The JAXR API consists of two entities:
- JAXR client. This is a client application that uses the JAXR API to access a business registry through a JAXR provider.
- JAXR provider. This is a server-side implementation of the JAXR API that provides access to a one or more registry providers that are based on a common specification.
A JAXR client uses special interfaces within the JAXR API to access the JAXR provider. The JAXR provider then communicates with a registry.
Note that the latest JAXR reference implementation (JAXR 1.0.7) which is available in the Java Web Services Developer Pack 1.5, contains a JAXR provider for UDDI registries only, not ebXML. If you want more information on UDDI, visit the OASIS UDDI website.
According to the JAXR specifications, a provider must implement key interfaces in two packages:
javax.xml.registry. This consists of the API interfaces and classes that define the registry access interface.
javax.xml.registry.infomodel. This consists of interfaces that define the information model for JAXR. These interfaces define the types of objects that reside in a registry and how they relate to each other. The basic interface in this package is the RegistryObject interface. Its subinterfaces include Organization, Service, and ServiceBinding.
The javax.xml.registry package is particularly important because it contains a number of useful client interfaces:
Connection. This interface represents a client session with a registry provider. The client must create a connection with the JAXR provider to use a registry.
RegistryService. The client obtains a RegistryService object from its connection. The RegistryService object enables the client to obtain the interfaces it uses to access the registry, such as BusinessQueryManager and BusinessLifeCycleManager.
BusinessQueryManager. An object that implements this interface allows the client to search a registry for information in accordance with the javax.xml.registry.infomodel interfaces.
BusinessLifeCycleManager. An object that implements this interface allows the client to modify or delete the information in a registry.
Making the JAXR Connection
A JAXR client starts by creating an instance of the abstract class ConnectionFactory:
import javax.xml.registry.*;
...
ConnectionFactory connFactory =
ConnectionFactory.newInstance();
Next, the client creates a set of properties that specify the URL or URLs of the registry or registries being accessed. For example, the following code provides the URLs of the query service and publishing service for the IBM test registry:
Properties props = new Properties();
props.setProperty("javax.xml.registry.queryManagerURL",
"http://uddi.ibm.com/testregistry/inquiryapi");
props.setProperty("javax.xml.registry.lifeCycleManagerURL",
"https://uddi.ibm.com/testregistry/publishapi");
You can specify proxy host and port information for the network on which the client is running, if needed (for example, if behind a firewall). For queries, the client might need to specify only the HTTP proxy host and port. For updates, the client typically also needs to specify a HTTPS proxy host and port:
props.setProperty("com.sun.xml.registry.http.proxyHost",
"myhost.mydomain");
props.setProperty("com.sun.xml.registry.http.proxyPort",
"8080");
props.setProperty("com.sun.xml.registry.https.proxyHost",
"myhost.mydomain");
props.setProperty("com.sun.xml.registry.https.proxyPort",
"8080");
The client then sets the properties for the connection factory and creates the connection:
connFactory.setProperties(props);
Connection connection = connFactory.createConnection();
After creating the connection, the client uses the connection to obtain a RegistryService object. It then uses the RegistryService to get objects that implement the appropriate query interfaces:
RegistryService rs = connection.getRegistryService();
BusinessQueryManager bqm = rs.getBusinessQueryManager();
BusinessLifeCycleManager blcm =
rs.getBusinessLifeCycleManager();
If a client need to both read and write data to a registry, it should obtain both a BusinessQueryManager object and a BusinessLifeCycleManager object from the RegistryService object. If the client is only performing simple queries, it typically needs only a BusinessQueryManager object.
Querying a Registry
The BusinessQueryManager interface supports a number of methods that allow clients to search for data. Many of these methods return a BulkResponse (a collection of objects) that meets a set of criteria specified in the method arguments. The most useful of these methods are:
findOrganizations. This method returns a list of organizations that meet the specified criteria--often a name pattern or a classification within a classification scheme.
public BulkResponse findOrganizations(
Collection findQualifiers,
Collection namePatterns,
Collection classifications,
Collection specifications,
Collection externalIdentifiers,
Collection externalLinks)
throws JAXRException
findServices. This method returns a set of services offered by a specified organization.
public BulkResponse findServices(Key orgKey,
Collection findQualifiers,
Collection namePatterns,
Collection classifications,
Collection specifications)
throws JAXRException
findServiceBindings. This method returns the service bindings (information about how to access the service) that are supported by a specified service.
public BulkResponse findServiceBindings(Key serviceKey,
Collection findQualifiers,
Collection classifications,
Collection specifications)
throws JAXRException
To search for organizations by name, use a combination of find qualifiers and name patterns. The findOrganizations() method takes a collection of findQualifier objects as its first argument, and a collection of namePattern objects as its second argument. A client can use percent signs (%) to specify that the query string can occur anywhere within the organization name. For example, the following code fragment performs a case-sensitive search for organizations whose names contain the characters contained in qString:
ArrayList qualifiers = new ArrayList();
qualifiers.add(FindQualifier.CASE_SENSITIVE_MATCH);
qualifiers.add(FindQualifier.SORT_BY_NAME_DESC);
ArrayList names = new ArrayList();
names.add("%" + qString + "%");
BulkResponse response =
bqm.findOrganizations(
qualifiers, names, null, null, null, null);
Collection orgs = response.getCollection();
You can also find organizations by classification. To do that, you need to establish the classification within a particular classification scheme, and then specify the classification as an argument to the findOrganizations() method. The following code fragment finds all organizations that correspond to a particular classification within the North American Industry Classification System (NAICS) taxonomy.
ClassificationScheme cScheme =
bqm.findClassificationSchemeByName(null,
"ntis-gov:naics");
Classification classification =
blcm.createClassification(cScheme,
"Snack and Nonalcoholic Beverage Bars", "722213");
Collection classifications = new ArrayList();
classifications.add(classification);
BulkResponse response = bqm.findOrganizations(null,
null, classifications, null, null, null);
Collection orgs = response.getCollection();
After a client locates an organization, it can find that organization's services. There can be several ways to invoke a business service, and each invocation is described in UDDI by a binding template. ServiceBinding instances are RegistryObjects that map to UDDI binding templates, and represent technical information on how to invoke a service.
Iterator orgIter = orgs.iterator();
while (orgIter.hasNext()) {
Organization org = (Organization) orgIter.next();
Collection services = org.getServices();
Iterator svcIter = services.iterator();
while (svcIter.hasNext()) {
Service svc = (Service) svcIter.next();
Collection serviceBindings =
svc.getServiceBindings();
Iterator sbIter = serviceBindings.iterator();
while (sbIter.hasNext()) {
ServiceBinding sb =
(ServiceBinding) sbIter.next();
}
}
}
At this point, the source code must follow the standards for the registry type in question. So to continue the UDDI example, the developer could access the tModel information for the ServiceBinding and use it to obtain exact information on how to invoke the service.
For more information, about:
- JAXR: see the JAXR page.
- UDDI: see UDDI.org and the registry finder page. The servers that are listed on the registry finder page as "Inquiry API" and "Publish API" are the URLs that you would enter into the properties file in the source code to perform a successful JAXR connection. Note that you might have to register to obtain a username and password with the company hosting the UDDI server. This is also a good site to get information on low-level UDDI web service constructs, such as
tModels.
- EbXML: the EbXML Page.
Sample Code
The sample code for this Tech Tip uses JAXR to query a registry for organizations whose name matches the name pattern "%USA%".
To run the sample code:
- Download the sample archive for this Tech Tip.
- Download and install the JWSDP 1.5.
- Change to the directory where you downloaded the sample archive. Uncompress the JAR file for the sample archive as follows:
jar xvf ttmar2005jaxr.jar
- Add the following JWSDP 1.5 libraries to your
CLASSPATH environment variable. The locations are given relative to the base directory (jwsdp-1.5):
jaxr/lib/jaxr-api.jar
jaxr/lib/jaxr-impl.jar
jaxp/lib/jaxp-api.jar
jaxp/lib/endorsed/xercesImpl.jar
jaxp/lib/endorsed/xalan.jar
jaxp/lib/endorsed/dom.jar
jaxp/lib/endorsed/sax.jar
jwsdp-shared/lib/mail.jar
jwsdp-shared/lib/activation.jar
jwsdp-shared/lib/jaas.jar
jwsdp-shared/lib/commons-logging.jar
saaj/lib/saaj-api.jar
saaj/lib/saaj-impl.jar
jaxb/lib/jaxb-api.jar
jaxb/lib/jaxb-impl.jar
jaxb/lib/jaxb-libs.jar
jaxb/lib/jaxb-xjc.jar
- Compile the
BusinessQueryTest.java file.
- If you are behind a firewall, be sure to enter the appropriate proxy information inside the source code. Otherwise, the
JAXR API will not be able to connect to the outside server.
- Run
BusinessQueryTest:
java BusinessQueryTest
You should see results that begin like this:
Successfully queried the registry for organization matching
the name pattern: "%USA%"
Results found: 3
Organization Name: USA-Works
Organization Key: 8cd1668c-64fa-4aa5-a053-bd0be4bd53f5
Organization Description: Liberty and Freedom
Service Name: Federal Government Service
Service Key: 056b2905-3999-4912-89de-fe79cbfedea0
Service Description: Services of the Federal Government
|
|
 |
 |
|
|
 |
 |
IMPORTANT: Please read our Licensing, Terms of Use, and Privacy policies:
http://developer.java.sun.com/berkeley_license.html
http://www.sun.com/share/text/termsofuse.html
Privacy Statement: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your email preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe.
Comments? Send your feedback on the Enterprise Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=newslet
Subscribe to other Java developer Tech Tips:
- Core Java Technologies Tech Tips. Get tips on using core Java technologies and APIs, such as those in the Java 2 Platform, Standard Edition (J2SE).
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the Sun Developer Network - Subscriptions page, choose the newsletters you want to subscribe to and click "Submit".
- To unsubscribe, go to the Subscriptions page, uncheck the appropriate checkbox, and click "Submit".
ARCHIVES: You'll find the Enterprise Java Technologies Tech Tips archives at:
http://java.sun.com/developer/EJTechTips/index.html
Copyright 1994-2006 Sun Microsystems, Inc. All
rights reserved.
4150 Network Circle, Santa Clara, CA 95054 USA.
This document is protected by Copyright 1994-2006 Sun Microsystems, Inc. in the United States and other countries.
|