Configuring and Using Oracle 10g AS JMS

As discussed earlier, the default JMS provider, Oracle 10g AS JMS, is enabled out of the box. To disable JMS in your environment simply comment out the following line in the server.xml file:

 <jms-config path="./jms.xml"/> 

From the preceding code you can probably tell that the path to the Oracle 10g AS JMS configuration file is jms.xml and that the file is stored in the same directory as server.xml . You can change the location of the configuration file by changing the path attribute to the path you want. However, you should think twice about doing this since you'll be moving the file out of its standard location, which may confuse others who have to manage your server. Also remember that the default configuration keeps the JMS configuration in the same place as all the other configuration files so why change this?

Standard Configuration

The standard jms.xml file looks like this:

 <?xml version="1.0" standalone='yes'?> <!DOCTYPE jms-server PUBLIC "OC4J JMS server"                        "http://xmlns.oracle.com/ias/dtds/jms-server-9_04.dtd"> <jms-server port="9127">     <!-- Queue bindings, these queues will be bound to their respective          JNDI path for later retrieval -->     <queue name="Demo Queue" location="jms/demoQueue">         <description>A dummy queue</description>     </queue>     <!-- Topic bindings, these topics will be bound to their respective          JNDI path for later retrieval -->     <topic name="Demo Topic" location="jms/demoTopic">         <description>A dummy topic</description>     </topic>     <!-- path to the log file where JMS-events/errors are stored -->     <log>         <file path="../log/jms.log"/>         <!-- Uncomment this if you want to use ODL logging capabilities         <odl path="../log/jms/" max-file-size="1000" max-directory-size="10000"/>         -->     </log>     <queue name="jms/OracleSyndicateQueue" location="jms/OracleSyndicateQueue">         <description>Oracle Syndication Services Queue</description>     </queue>     <!--     <queue-connection-factory name="jms/OracleSyndicateQueueConnectionFactory"        location="jms/OracleSyndicateQueueConnectionFactory"/>    -->     <queue-connection-factory location="jms/OracleSyndicateQueueConnectionFactory"/>     <queue name="jms/OracleUddiReplicationQueue"         location="jms/OracleUddiReplicationQueue">         <description>Queue for replication scheduler</description>     </queue>     <!--   <queue-connection-factory     name="jms/OracleUddiReplicationQueueConnectionFactory"     location="jms/OracleUddiReplicationQueueConnectionFactory"/>     -->     <queue-connection-factory         location="jms/OracleUddiReplicationQueueConnectionFactory"/>     <queue name="jms/OracleWebClippingQueue"         location="jms/OracleWebClippingQueue">         <description>Queue for Web Clipping</description>     </queue>     <!--     <queue-connection-factory       name="jms/OracleWebClippingQueueConnectionFactory"       location="jms/OracleWebClippingQueueConnectionFactory"/>       -->     <queue-connection-factory       location="jms/OracleWebClippingQueueConnectionFactory"/> </jms-server> 

Due to the good use of XML, the configuration file is fairly self-explanatory. The <queue> elements are used to configure queues for point-to-point messaging, and the <topic> elements are used to configure topics for publish/subscribe messaging. Queues and topics are collectively known as destinations . Connection factories are configured using the <queue-connection-factory> and <topic-configuration-factory> tags.

For those of you who aren't totally familiar with the nuances of JMS, connection factories are used to obtain a connection to particular resource, be it a queue or a topic. The Queue and Topic interfaces defined in the JMS specification serve as an abstraction of the provider-specific destination name, not of the resource itself. The QueueConnection and TopicConnection interfaces encapsulate access to the resources identified by Queue and Topic objects. The QueueConnectionFactory and TopicConnectionFactory interfaces are designed to encapsulate provider-specific logic for obtaining a connection to the destination and to present a unified API to Java applications. Without these interfaces your application would need to understand all the different, provider-specific, mechanisms for obtaining a connection to a destination.

Unless you want to download and try out the examples from the Oracle website then you can safely remove the <queue> and <topic> tags for demoQueue and demoTopic . However, you should leave the other <queue> and <queue-connection-factory> tags (for the Web Clipping, Syndication Service and the UDDI replication service queues) in place, or at the most, comment them out so you can easily put them back in, because these features use them internally.

To define transactional connection factories you can use the <xa-connection-factory>, <xa-queue-connection> , and <xa-topic-connection-factory> elements.

Use these factories if you want your queues to participate in Java Transaction Service (JTS) managed transactions. This can be useful in applications in which messaging is part of the critical application logic and must be performed as part of larger, atomic blocks, perhaps in conjunction with some kind of database management system (DBMS). Configuring the XA factories is no more complicated than configuring their nontransactional counterparts, but you should only use the XA versions if your messaging application definitely requires transaction support. Don't be tempted to use the XA versions "just in case" you might need transaction support in future. Transactions add a performance overhead, which is completely unnecessary if your application doesn't need them. As you'll see, by coding to interfaces ( Connection and ConnectionFactory ) and using appropriate configuration, you can completely change the destination configuration without affecting the code.

In addition to whatever you configure in jms.xml , you'll find six built-in connection factories that your applications can use. These are available at the following JNDI locations:

  • jms/ConnectionFactory

  • jms/QueueConnectionFactory

  • jms/TopicConnectionFactory

  • jms/XAConnectionFactory

  • jms/XAQueueConnectionFactory

  • jms/XATopicConnectionFactory

Each one of these is bound to an implementation of the corresponding interface. For the most part these predefined connection factories will serve the needs of your application. However, if you need settings other than the defaults, then just configure your own connection factories in jms.xml .

In addition to these connection factories, there's also a default queue where all undelivered messages get placed. If you find that some of your messages just aren't getting through and you can't see why, check the jms/Oc4jJmsExceptionQueue queue. If your messages are in there, then it's likely that your code is correct but that you have a configuration error that's causing the messages to be undeliverable. If the message isn't in this queue, then the likelihood is that the message never reached Oracle 10g AS, which points to a coding error.

Building and Configuring an Application

In the previous section you looked at the basics of configuration. In this section you'll put that knowledge to use and build and configure a full point-to-point JMS-based application. This application will implement two command-line tools: one that sends a message containing "Hello World" to a queue and another that receives and displays a message from the same queue. Along the way, we'll show you how you can use the JMS command-line management tools to check that your application is functioning correctly.

Creating a Queue

First, you need to configure the queue for the "Hello World" messages. Do this by adding a <queue> tag to the jms.xml configuration file, as shown here:

 <queue name="Hello World Queue" location="jms/helloWorldQueue">         <description>A queue for the hello world "application"</description>     </queue> 

You can check that the queue is configured correctly be firing up OC4J and running the following command from the command line:

 java -cp "$J2EE_HOME/oc4j.jar" com.evermind.server.jms.JMSUtils /  -username admin -password welcome destinations 

You should see the helloWorldQueue entry appear in the list. As far as server configuration goes, that's all that's required. The bulk of the configuration is done in the application configuration files.

Note 

The JMSutils tool, used here to check the existence of the queue, performs lots of useful operations on the Oracle 10g AS JMS server. We've described a few of the more useful ones in this chapter, but you'll find a full list in the documentation.

Creating a Base Class

Both of the command-line tools you'll be implementing (the sender and the receiver) will need to look up the queue and get QueueConnection objects, so it's easiest to put this functionality into a base class, as follows :

 package com.apress.oracle10g.jms; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; public abstract class AbstractJMSClient {     private static final String FACTORY_NAME =                 "java:comp/env/jms/QueueConnectionFactory";     private static final String QUEUE_NAME = "java:comp/env/jms/helloWorldQueue";     private QueueConnectionFactory factory = null;     protected Queue getQueue() throws NamingException {         Context ctx = new InitialContext();         //get the connection factory         QueueConnectionFactory factory = getQueueConnectionFactory();         // get the queue         Queue q = (Queue) ctx.lookup(QUEUE_NAME);         return q;     }     protected QueueConnection getQueueConnection() throws NamingException,             JMSException {         return getQueueConnectionFactory().createQueueConnection();     }     private QueueConnectionFactory getQueueConnectionFactory()             throws NamingException {         Context ctx = new InitialContext();         factory = (QueueConnectionFactory) ctx.lookup(FACTORY_NAME);         return factory;     } } 

This code simply wraps the JNDI lookups for the connection factory and for the queue. Notice that it uses local JNDI names (which start with java:comp/env ) rather than referring to the actual JNDI locations specified in the configuration file. Later, you'll map these local names to real locations in your deployment descriptors, using <resource-ref> and <resource-ref-mapping> elements. This indirection gives you the flexibility to change what these names map to without changing any code. It also makes your code more portable between application servers. Of course, if you're just whipping up a quick test client and know that you won't need to change the JNDI locations, then you can just hard-code the actual JNDI locations instead. Check out Chapter 5 for more tips on using JNDI.

Implementing the Sender

Now you can create a simple sender client, as shown here:

 package com.apress.oracle10g.jms; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import javax.naming.NamingException; public class SendHelloWorld extends AbstractJMSClient {     public static void main(String[] args) throws Exception {         SendHelloWorld sender = new SendHelloWorld();         sender.send();     }     public void send() throws JMSException, NamingException {         // get queue and connection         Queue q = getQueue();         QueueConnection connection = getQueueConnection();         // start...         connection.start();         QueueSession session = connection.createQueueSession(false,                 Session.AUTO_ACKNOWLEDGE);         // send         QueueSender sender = session.createSender(q);         TextMessage msg = session.createTextMessage();         msg.setText("Hello World!");         sender.send(msg);         // close         sender.close();         session.close();         connection.close();         System.out.println("Message Sent");     } } 

The preceding code simply uses the queue and queue connection provided by the base class to create and send a textual message containing the words "Hello World."

Configuring an Application Client

At this point you're almost ready to try out the code; all that's left to do is to create the application configuration files. The first one to create is jndi.properties , which contains the JNDI connection details, as follows:

 java.naming.factory.initial=   com.evermind.server.ApplicationClientInitialContextFactory java.naming.provider.url=ormi://localhost/ java.naming.security.principal=admin java.naming.security.credentials=welcome 

The jndi.properties specifies the name of the JNDI context factory class, along with the URL, username, and password required to connect to the JNDI server. If you have OC4J running on a different machine or if you need to use a different username or password, then you should make the appropriate changes in your file.

Next up, as shown in the following code sample, is the standard J2EE application-client .xml file, which is required by all standalone J2EE client applications:

 <?xml version="1.0"?> <!DOCTYPE application-client PUBLIC       "-//Sun Microsystems, Inc.//DTD J2EE Application Client 1.2//EN"       "http://java.sun.com/dtd/application-client_1_3.dtd"> <application-client>     <display-name>Hello World</display-name>     <resource-ref>         <res-ref-name>jms/QueueConnectionFactory</res-ref-name>         <res-type>javax.jms.QueueConnectionFactory</res-type>         <res-auth>Container</res-auth>         <res-sharing-scope>Shareable</res-sharing-scope>     </resource-ref>     <resource-env-ref>         <resource-env-ref-name>jms/helloWorldQueue</resource-env-ref-name>         <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>     </resource-env-ref> </application-client> 

In this file you're telling OC4J that this application client will need to be able to look up a connection factory at jms/QueueConnectionFactory and a queue at jms/helloWorldQueue . Note that the names used here are the local JNDI names (minus the java:comp/env prefix ) that your application uses, not the real locations specified in jms.xml .

Next, create an orion-application.xml file. Inside it, map these local JNDI names to real locations, as follows:

 <?xml version="1.0"?> <!DOCTYPE orion-application-client PUBLIC "-//Evermind//DTD J2EE Application-client runtime 1.2//EN" "http://xmlns.oracle.com/ias/dtds/orion-application-client.dtd"> <orion-application-client>  <resource-ref-mapping name="jms/QueueConnectionFactory"  location="jms/QueueConnectionFactory"/>  <resource-env-ref-mapping name="jms/helloWorldQueue"  location="jms/helloWorldQueue"/> </orion-application-client> 

Running the Client

Once the code is compiled you can run it with the following:

 java -cp "classes:src/META-INF:$J2EE_HOME/oc4j.jar" \         com.apress.oracle10g.jms.SendHelloWorld 

This command assumes that the class files for your application are under the classes directory and that your application-client.xml and orion-application.xml configuration files are in the src/META-INF directory. If you've used a different directory structure, edit the cp (classpath) parameter accordingly .

If the application runs successfully you'll receive a "Message Sent" message. If you don't get this message, check that the classpath is specified correctly, that OC4J is running, and that the queue is configured correctly. To check that the message actually made it to your queue, you can use the browse command of JMSutils, as shown here:

 java -cp "$J2EE_HOME/oc4j.jar" com.evermind.server.jms.JMSUtils -username admin / -password welcome browse "Hello World Queue" 

The important thing to notice about the use of the browse command is that instead of the using the JNDI name for the queue, you have to use the "friendly" name that you specified in the name attribute of the <queue> tag. This means that if your name contains spaces, you need to enclose it in quotes. For this reason it's generally easier to use names that don't contain spaces. Running the browse command should yield output like this:

 <textmessage>     <header>         <JMSCorrelationID value="" />         <JMSDeliveryMode  value="PERSISTENT" />         <JMSDestination   value="Queue[Hello World Queue]" />         <JMSExpiration    value="0" />         <JMSMessageID         value="ID:Oc4jJMS.Message.python.local.6b352c:fc0725fdff:-8000.4" />         <JMSPriority      value="4" />         <JMSRedelivered   value="false" />         <JMSReplyTo       value="null" />         <JMSTimestamp     value="1082451689466" />         <JMSType          value="" />     </header>     <properties>         <string    key="JMSXConsumerTXID" value="" />         <int       key="JMSXDeliveryCount" value="1" />         <string    key="JMSXProducerTXID" value="" />         <long      key="JMSXRcvTimestamp" value="1082451693818" />         <string    key="JMSXUserID" value="" />         <string    key="JMS_OC4J_Type" value="textmessage" />     </properties>     <textbody>         <string    value="Hello World!" />     </textbody> </textmessage> 1 messages processed 

Near the bottom of the message you should see the <textbody> element, which contains the value of the message in a <string> tag. This output contains a lot of information about the message that can be used for diagnostic purposes if you're experiencing problems with your JMS application.

Implementing the Receiver

Now that you have a message in the queue you can build a receiver to consume the message and display it in the console. The code for the ReceiveHelloWorld class is very similar to that of the SendHelloWorld class, as shown here:

 package com.apress.oracle10g.jms; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueReceiver; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import javax.naming.NamingException; public class ReceiveHelloWorld extends AbstractJMSClient{     public static void main(String[] args) throws Exception {         ReceiveHelloWorld recevier = new ReceiveHelloWorld();         recevier.receive();     }     public void receive() throws NamingException, JMSException {         // get queue and connection         Queue q = getQueue();         QueueConnection connection = getQueueConnection();         // start...         connection.start();         QueueSession session = connection.createQueueSession(false,                 Session.AUTO_ACKNOWLEDGE);         // receive         QueueReceiver receiver = session.createReceiver(q);         TextMessage msg = (TextMessage)receiver.receiveNoWait();         if(msg != null)             System.out.println("Received: " + msg.getText());         else             System.out.println("No message received");         // close         receiver.close();         session.close();         connection.close();     } } 

The main difference, of course, is that this class receives a message from the queue instead of sending one to it. Run the receiver using the following command in order to receive the message you just sent:

 java -cp "bin:src/META-INF:$J2EE_HOME/oc4j.jar" \               com.apress.oracle10g.jms.ReceiveHelloWorld 

You should receive a message informing you whether or not a message was received from the queue. Running the JMSutils browse command again should now show that no messages are left in the queue.

As you can see, getting a basic JMS-based application up and running is easy. In the rest of this section you'll look at additional configuration options.

Configuring File Persistence

The queue you created in the previous example was an in-memory queue. This means that if you send a message to the queue and then shut the server down, the message will be gone when you restart the server. In some cases this may be the desired effect, but in others you may wish to have some level of protection for failures in the server. Using file persistence, you can instruct Oracle 10g AS to persist messages in a particular queue or topic to the file system.

To see this in action, you should first try sending a message to helloWorldQueue using the sender, and then shut down and restart OC4J. When you restart, use the JMSutils browse command to check the contents of the queue. You should find nothing there.

To turn on file persistence for the helloWorldQueue , make the following modification to the jms.xml file:

 <queue name="Hello World Queue" location="jms/helloWorldQueue"          persistence-file="helloWorld.queue">       <description>A queue for the hello world "application"</description>     </queue> 

Notice that the <queue> element now contains a persistence-file attribute that contains the path to the file where messages should be stored. If the file doesn't exist, then Oracle 10g AS will create it for you. The path specified can be either an absolute path or a path relative to the default persistence location specified in application.xml . You can change the default location by modifying the following entry in application.xml :

 <persistence path="../persistence"/> 

Once you've updated jms.xml to specify a persistence file, restart the application server and send another message to the helloWorldQueue . Use the JMSutils browse command to verify that the message made it to the queue. Then restart the server and use the browse command to verify that unlike before, the message is still in the queue.

Persistence File Management

The file format used by Oracle 10g AS is a vaguely decipherable binary format. Open the file up in a text editor and you'll be able to make out the gist of each message in between a mass of undisplayable characters . Although you could probably edit these files there would be no guarantee that it would work all the time and we certainly wouldn't recommend it.

You're free to copy or delete persistence files when they aren't in use, but doing so while Oracle 10g AS is using them will result in an unrecoverable error.

Provided you haven't disabled locking, a lock file that's used to synchronize access across multiple instances of Oracle 10g AS will accompany each queue. Lock files have the same name as the queue persistence file; they just have .lock bolted on at the end. In general, you should avoid playing with these files while the server is running. A normal shutdown of Oracle 10g AS will clean up the lock files used by the queues. However if Oracle 10g AS terminates without a proper shutdown, either because of an error or because you've killed the process, then some lock files may be left lying around. You should delete these before you restart the Oracle 10g AS server, since the state they contain will be inconsistent with the runtime environment.

Be aware that there's a limit on the number of active file handles that Oracle 10g AS will maintain for persistent destinations. By default this is set to 64, meaning that Oracle will only maintain 64 active file handles for persistence. If you have more than this number of persistent queues then Oracle will open and close files as appropriate, which can slow down message delivery. You can change the maximum number of allowable file handles by setting the oc4j.jms.maxOpenFiles system property when starting Oracle 10g AS. This is useful if your operating system won't allow 64 open file handles for the process or if you wish to increase the number (within the bounds of the operating system) to increase performance.

If you have a large number of persistent queues, then you should consider using OJMS (covered later in this chapter) instead of the default Oracle 10g AS JMS provider. OJMS is built on top of the Oracle database and is more reliable than simple file-based persistence.

Configuring Hosts and Ports

By default Oracle 10g AS JMS will listen on port 9127 for all network interfaces in the machine. You can change the port number by editing the port attribute of the root <jms> tag in jms.xml as follows:

 <jms-server port="9127"> 

If you want Oracle 10g AS JMS to listen on a single network interface, then you can also set the host attribute of the <jms> tag, as shown here:

 <jms-server port="9127" host="192.168.0.254"/> 

By default, connections obtained through connection factories will use the local JMS services configured in the jms-server element. To force all connections to go to a different server, adjust the host and port attributes on the connection factory element. For example, to force all connections obtained through myQueueConnectionFactory to go to the server listening on 192.168.1.57:9127 , you might specify the following:

 <queue-connection-factory location="jms/myQueueConnectionFactory"                                            host="192.168.1.57" port="9127"/> 

This can be useful if you wish to redirect JMS operations to a different server, and is a valid reason for specifying a custom connection factory rather than using the default factories provided by Oracle 10g AS JMS.

Configuring Logging

You can configure email and file-based logging using the <log> element within jms.xml . By default, OC4J JMS logs to /j2ee/home/log/jms.log . This file is specified relative to jms.xml , like this:

 <log><file path="../log/jms.log" /></log> 

If a mail session has been configured, events can also be logged to an email address, like this:

 <log>     <file path="../log/jms.log" />     <mail address="admin@somewhere.com" /> </log> 

By default the logging output includes WARNING, ERROR , and CRITICAL events, meaning that it only contains information when something goes wrong. You can increase the verbosity ofm the log by setting the oc4j.jms.debug system property to true . This will include all NORMAL level eventsbasically a trace of the actions taken by Oracle 10g AS JMS. When you set oc4j.jms.debug to true the log messages are written to stderr as well as to the log file.

None of the log levels will write the message contents to the log. If you want to check the contents of a queue, then you need to use the JMSutils command-line tool that you saw earlier.



Oracle Application Server 10g. J2EE Deployment and Administration
Oracle Application Server 10g: J2EE Deployment and Administration
ISBN: 1590592352
EAN: 2147483647
Year: 2004
Pages: 150

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net