How to Configure and Use Oracle's JMS Connector with Tibco Enterprise for JMS

Date: 02/02/06
Author: Jeff Steidl

Introduction

This document and accompanying demo application show how to configure Oracle's JMS Connector implementation for Tibco Enterprise for JMS. They also demonstrate how to develop, assemble and configure a JMS application.

The demo application simulates the playing of a simple numbers game.

The game rules are as follows: There are 3 players and one dealer. During phase one, the dealer provides each participant (including the dealer) a random number from 1 to 100. During phase two each participant may either keep their number from phase one, or get a new random number. Phase two is performed one participant at a time, and a participant is always aware of the other participant's current values. The goal of the game is have the highest number at the end of phase two.

In the demo application, an MDB is the dealer. Three app-client instances are the players. The players send requests (join game, draw, pass) to the MDB via a JMS queue. The MDB sends global game state information (join request accepted/rejected, random number values, player turn notification) to the app-clients via a JMS topic.

Prerequisites

Knowledge

  • You should be familiar with these technologies: JMS 1.1, MDBs (EJB 2.1), J2EE Connector Architecture 1.5
  • ant
  • jar file structure, including the use of MANIFEST.MF files and 'Class-Path's

Software Requirements

This demo requires that the following sofware components are installed and configured correctly:

Notations

This document uses ellipses inside of an xml block:

   <some-element>
      ...
   </some-element>


Ellipses may also be used to indicate the context of an XML block:

   <some-element>
      ...
      <some-sub-element>
         SubElementSetting
      </some-sub-element>

In this case <some-sub-element> is being discussed, and <some_element> is only shown to indicate the context of <some-sub-element> .

The following are used to represent directories:

  • %ORACLE_HOME% - The directory where you installed OC4J.
  • %JAVA_HOME% - The directory where your JDK is installed
  • %HOWTO_HOME% - The directory where this demo is unzipped

Building the Application

Developing a J2EE application which uses JMS and a custom JMS Connector configuration involves the following:

This document will focus on those aspects of the above steps that are necessary for creating a J2EE application which makes use of Oracle's JMS Connector with Tibco Enterprise for JMS.

Developing the Application Components

The Application Component Provider develops application components such as MDBs and app-clients. For application components using JMS, the following tasks are typically performed as part of that process:

  1. Write Code to Send and Receive Messages
  2. Declare Logical Names for JMS Resources
  3. Use Logical Names for JMS Resources
  4. Create and Declare an MDB Class

This section concludes with some key points concerning the development of application components.

Application Component Provider Task #1: Write Code to Send and Receive Messages

Writing code that sends and receives JMS messages requires using the following types of objects:

The remainder of this section shows how the object types and activities in the above table map to the MyChannel class (see MyChannel.java ).

Class Variables

As per the "Use After Demo Initialization" column in the above table, the Connection, Session, MessageProducer and MessageConsumer objects are the only ones used after initialization is done, so only those are declared as class variables in MyChannel:

    private Connection connection;
    private Session session;
    private MessageProducer producer;
    private MessageConsumer consumer;

The remaining objects are local variables within the MyChannel constructor where initialization is performed.

Initialization

Initialization involves the look-up and creation of objects as per the "Use During Demo Initialization" column in the above table:

    public MyChannel(String connectionFactoryName, String destinationName) throws Exception {

        Context ctx = new InitialContext();

        // Get the destination.
        Destination destination = (Destination) ctx.lookup(destinationName);

        // Get the connection factory.
        ConnectionFactory cf = (ConnectionFactory) ctx.lookup(connectionFactoryName);

        // Use the connection factory to create a connection.
        connection = cf.createConnection();

        // Start the connection.
        connection.start();

        // Use the connection to create a session.
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        // Use the session and destination to create a message producer and a message consumer.
        producer = session.createProducer(destination);
        consumer = session.createConsumer(destination);
    }

Note that connectionFactoryName and destinationName are the JNDI locations of references to the connection factory and destination, respectively. We will get back to those later.

The above initialization code is just a simple example. A small sample of other activities that may be performed by real initialization code is:

  • Save the looked-up connection factory and destination and use them several times.
  • Set up a javax.jms.MessageListener. ( MessageListeners are only for use with app-clients. For asynchronous reception on the server side use MDBs.)
  • Create multiple producers and/or consumers from the same session.
  • Create a consumer with a message selector to filter out unwanted messages.
  • Allow for durable subscribers.
  • Perform error handling/recovery.

Also, real code may not limit the above activities to "initialization" code. For example:

  • Different MessageConsumers may be created on-the-fly in order to use different message selectors.
  • Temporary destinations may be created on-the-fly to handle responses to requests. (When persistence is desired, non-temporary destinations are generally required. In that case responses and requests are typically matched with dynamic MessageConsumers as described in the previous case - using a message selector to find the correct response, for example by matching the received JMSCorrelationID to the sent JMSMessageID.)
Sending Messages

As per the "Use After Demo Initialization" column in the above table, the Session object ( session) is used to create messages and the MessageProducer object ( producer) is used to send messages:

    public void send(Serializable obj) throws JMSException {

        // Use the session to create a new message.
        ObjectMessage msg = session.createObjectMessage(obj);

        // Use the message producer to send the message.
        producer.send(msg);
    }

The above message production code is just a simple example which creates and sends an ObjectMessage (where the message payload can be any Serializable object). Other activities that may be performed by real message production code include:

  • Produce other message types (with different types of payloads) such as BytesMessage, MapMessage, StreamMessage and TextMessage.
  • Control message production properties such as delivery mode, priority and time-to-live.
  • Set message header fields (such as JMSCorrelationID).
  • Set message properties (such as an application-specific CustomerID property).
  • Disable/enable message ID and time-stamp generation.
  • Perform error handling/recovery.
Receiving Messages

As per the "Use After Demo Initialization" column in the above table, the MessageConsumer object ( consumer) is used to receive messages:

    public Serializable receive() throws JMSException {

        // Use the message consumer to receive a message.
        ObjectMessage msg = (ObjectMessage) consumer.receive();

        return msg.getObject();
    }

The above message consumption code is just a simple example which waits forever for a message and then (assuming it's an ObjectMessage) extracts and returns the payload. Other activities that may be performed by real message consumption code include:

  • Consume other message types (with different types of payloads) such as BytesMessage, MapMessage, StreamMessage and TextMessage.
  • Use other MessageConsumer receive variants (which do not wait, or only wait for limited amount of time).
  • Perform error handling/recovery. (For example, if the received message is not an ObjectMessage the above code will throw a ClassCastException which could be caught and handled.)
Releasing Resources

As per the "Use After Demo Initialization" column in the above table, the Connection object ( connection) is used to close everything:

    public void close() throws JMSException {

        // Close the connection (and all of its sessions, producers and consumers).
        connection.close();
    }

The above resource release code is just a simple example which closes everything (connection, sessions, producers, consumers) at once. Real resource release code may:

  • Use Session.close to close a session (and all of its producers and consumers). This releases the session (and producer/consumer) resources without closing the connection.
  • Use MessageProducer.close or MessageConsumer.close to close individual MessageProducers and MessageConsumers, respectively. This releases the individual producer/consumer resources without closing the session or connection.
  • Perform error handling/recovery.

Application Component Provider Task #2: Declare Logical Names for JMS Resources

In the previous section, the matter of the JNDI locations of the connection factory and destination was left open. The problem is that the Application Component Provider may not have any idea what the actual JNDI locations will be when the application is finally deployed. The solution is for the Application Component Provider to just make up a name, declare that name in a deployment descriptor, and leave the task of picking an actual JNDI location to the Deployer (who may also get some assistance from the Application Assembler). Each application component only sees its own logical names, so the Application Component Provider for one component need not be concerned about conflicts with the names used in other components (which may have been created by one or more independent Application Component Providers).

Note: While any name will work, the J2EE spec recommends that these names start with "jms/".

Declaring Logical Names for Connection Factories

Let's say that the Application Component Provider who wrote the app-client wanted to declare the logical name "jms/PlayerConnectionFactory" to represent (reference) a connection factory. This can be done by inserting the following into application-client.xml (as a sub-element of the <application-client> element):

    <resource-ref>
        <res-ref-name>jms/PlayerConnectionFactory</res-ref-name>
        ...
    </resource-ref>

Note: "resource-ref" is short for "resource manager connection factory reference". So this type of logical name is, more specifically, a resource manager connection factory reference name.

We have told the Deployer we need a connection factory, but not what type of connection factory. If the application component needs a QueueConnectionFactory and the Deployer gives it a TopicConnectionFactory the application isn't likely to work. To tell the Deployer that the application component can use any type of connection factory that implements the javax.jms.ConnectionFactory interface, add the <res-type> element:

    <resource-ref>
        <res-ref-name>jms/PlayerConnectionFactory</res-ref-name>
         
                         <res-type>javax.jms.ConnectionFactory</res-type>
        ...
    </resource-ref>
                      

Note: There is some subtlety going on with the element names. The first subelement name ( <res-ref-name> ) includes "ref" because it is the name of the reference, not the name of an actual resource manager connection factory. The second subelement name ( <res-type> ) does not include "ref" because it is the type (interface) required to be implemented by an actual resource manager connection factory, not the type of the reference. The main element ( <resource-ref> ) includes "ref" because it is declaring a reference, not an actual resource manager connection factory. This naming convention is used for the remaining elements in this section, as well as for the <message-destination-ref> -related elements discussed in the next section.

The last missing piece of information is who is responsible for "signing on to the resource manager". That is, who provides the username and password to the JMS resource provider when a Connection is created using this connection factory. Again, the Deployer needs to know -- if the application doesn't provide the username/password (and the resource provider requires it), then the Deployer needs to know to provide this information. (See "Oracle Containers for J2EE: Resource Adapter Administrator's Guide" for information on container-managed sign-on and the oc4j-ra.xml file.)

To tell the Deployer that the application component will not provide the username/password (and that the "Container" must be configured to do so), add:

        <res-auth>Container</res-auth>

To tell the Deployer that the application component will provide the username/password, add:

        <res-auth>Application</res-auth>

Currently OC4J does not support container-managed sign-on in the app-client container (it does support it in the Web and EJB containers), so we must use the following for the app-client:

    <resource-ref>
        <res-ref-name>jms/PlayerConnectionFactory</res-ref-name>
        <res-type>javax.jms.ConnectionFactory</res-type>
         
                         <res-auth>Application</res-auth>
    </resource-ref>
                      

The <resource-ref> element also has a <res-sharing-scope> sub-element which defaults to "Shareable". It should generally be left set that way (which can be accomplished by just omitting the sub-element). If the application component may modify connections created using this connection factory in a way which makes the connections unusable by other application components, then <res-sharing-scope> should be set to "Unshareable" (and as per J2EE4.6, the application component is not portable).

The last thing that needs to be done to make the above <resource-ref> example valid is to check that it is in the correct sequence with respect to the other sub-elements of the <application-client> element. This ordering requirement is rooted in the app-client schema file in the definition of the type corresponding to <application-client> . As per that document, the correct ordering for the sub-elements of <application-client> used by this demo is:

  1. <display-name> element(s)
  2. <resource-ref> element(s)
  3. <message-destination-ref> element(s)

Moving on to the MDB, let's say that the Application Component Provider who wrote the MDB wanted to declare the logical name "jms/DealerConnectionFactory" to represent (reference) a connection factory that implements the javax.jms.ConnectionFactory interface and has container-managed sign on. The syntax works the same as for the app-client case:

    <resource-ref>
        <res-ref-name>jms/DealerConnectionFactory</res-ref-name>
        <res-type>javax.jms.ConnectionFactory</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

For MDBs the <resource-ref> element must be placed in the ejb-jar.xml file under the MDB's <message-driven> element. Again there is an ordering requirement, this time rooted in the EJB schema file. As per that document, the correct ordering for the sub-elements of <message-driven> used by this demo is:

  1. <display-name> element(s)
  2. <ejb-name> element
  3. <ejb-class> element
  4. <transaction-type> element
  5. <message-destination-type> element
  6. <message-destination-link> element
  7. <activation-config> elements
  8. <resource-ref> element(s)
  9. <message-destination-ref> element(s)
Declaring Logical Names for Destinations

Let's say that the Application Component Provider who wrote the app-client wanted to declare the logical name "jms/PlayerCommandDestination" to represent (reference) a destination. This can be done by inserting the following into application-client.xml (as a sub-element of the <application-client> element):

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerCommandDestination</message-destination-ref-name>
        ...
    </message-destination-ref>

Note: "message-destination-ref" is short for "message destination reference". So this type of logical name is, more specifically, a message destination reference name.

As was the case for declaring logical names for connection factories, the Deployer needs more than just a name to go on. If the application component expects point-to-point semantics (a queue) and the Deployer gives it pub/sub semantics (a topic) the application isn't likely to work. To tell the Deployer (and Application Assembler who also plays a role in configuring destinations) that the application component expects the destination to have point-to-point semantics, add:

        <message-destination-type>javax.jms.Queue</message-destination-type>

To tell the Deployer (and Application Assembler) that the application component expects the destination to have pub/sub semantics, add:

        <message-destination-type>javax.jms.Topic</message-destination-type>

One more piece of information is still required, and that is how does the application component use the destination? Does it consume messages from the destination, produce messages to the destination, or both? To tell the Deployer (and Application Assembler) that the application component consumes messages from the destination, add:

        <message-destination-usage>Consumes</message-destination-usage>

To tell the Deployer (and Application Assembler) that the application component produces messages to the destination, add:

        <message-destination-usage>Produces</message-destination-usage>

To tell the Deployer (and Application Assembler) that the application component both consumes messages from and produces message to the destination, add:

        <message-destination-usage>ConsumesProduces</message-destination-usage>

If the application component wants to send messages to a queue, the finished <message-destination-ref> (as far as the Application Component Provider is concerned) is:

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerCommandDestination</message-destination-ref-name>
        <message-destination-type>javax.jms.Queue</message-destination-type>
        <message-destination-usage>Produces</message-destination-usage>
    </message-destination-ref>

The Application Component Provider creating the app-client would also like to declare a destination named "jms/PlayerResponseDestination" with pub/sub semantics from which the app-client can receive messages:

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerResponseDestination</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Consumes</message-destination-usage>
    </message-destination-ref>

The last thing that needs to be done to make the above <message-destination-ref> example valid is to check that it is in the correct sequence with respect to the other sub-elements of the <application-client> element.

Moving on to the MDB, let's say that the Application Component Provider who wrote the MDB wanted to declare the logical name "jms/ToPlayerDest" to represent (reference) a destination with pub/sub semantics to which the MDB will send messages. The syntax works the same as for the app-client case:

    <message-destination-ref>
        <message-destination-ref-name>jms/ToPlayerDest</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Produces</message-destination-usage>
    </message-destination-ref>

For MDBs the <message-destination-ref> element must be placed in the ejb-jar.xml file under the MDB's <message-driven> element. Again the correct sequence of elements must be used.

Application Component Provider Task #3: Use Logical Names for JMS Resources

After a logical name is declared, it can be used by the application component code to reference JMS resources. Logical names (references declared with <resource-ref> and <message-destination-ref> elements) are types of environment entries, and all environment entries are placed in the "java:comp/env/" JNDI subcontext. So in order for the app-client to use the logical names:

  • jms/PlayerConnectionFactory
  • jms/PlayerCommandDestination
  • jms/PlayerResponseDestination
  • java:comp/env/jms/PlayerConnectionFactory
  • java:comp/env/jms/PlayerCommandDestination
  • java:comp/env/jms/PlayerResponseDestination

In the app-client code for this demo ( Player.java ), the above JNDI locations are assigned to strings:

    String      factoryName = "java:comp/env/jms/PlayerConnectionFactory";
    String  commandDestName = "java:comp/env/jms/PlayerCommandDestination";
    String responseDestName = "java:comp/env/jms/PlayerResponseDestination";
MyChannel constructor
    MyChannel command  = new MyChannel(factoryName,  commandDestName);
    MyChannel response = new MyChannel(factoryName, responseDestName);
MyChannel constructor

(Note that the lookups will not actually work until some more configuration is performed. This is because the logical names have only been declared by the Application Component Provider, and do not yet reference anything. The Deployer will map those logical names such that they reference actual connection factories and destinations. This additional configuration is discussed in the Configuring the Application for Deployment section.)

Likewise, the logical names declared in ejb-jar.xml:

  • jms/DealerConnectionFactory
  • jms/ToPlayerDest
  • java:comp/env/jms/DealerConnectionFactory
  • java:comp/env/jms/ToPlayerDest
Dealer.java MyChannel constructor
   chan = new MyChannel("java:comp/env/jms/DealerConnectionFactory",
                        "java:comp/env/jms/ToPlayerDest");

Application Component Provider Task #4: Create and Declare an MDB Class

The previous three tasks do not apply when using an MDB to receive messages. This is because for MDB inbound messaging the EJB container is responsible for performing JNDI lookups, creating connections and sessions, and receiving messages. However, the MDB provider must create and declare an MDB class. The MDB class for this demo can be found in Dealer.java .

Creating an MDB Class

The MDB class must implement javax.ejb.MessageDrivenBean and javax.jms.MessageListener:

public class Dealer implements MessageDrivenBean, MessageListener {

MDBs are required to provide a no-argument constructor:

    public Dealer() {}

Implementing MessageDrivenBean means providing a setMessageDrivenContext method:

    private MessageDrivenContext m_ctx = null;

    public void setMessageDrivenContext(MessageDrivenContext ctx) {
        m_ctx = ctx;
    }

The MessageDrivenContext ( m_ctx) may be used later by the MDB. For example, it may be used to roll-back a global transaction:

    m_ctx.setRollbackOnly();

MDBs are required to provide an ejbCreate method. This method typically allocates resources needed by the MDB. Although the MDB class does not allocate resources for receiving messages, it may (as in this example) allocate resources for sending messages:

    MyChannel chan;

    public void ejbCreate() {
       try {
           chan = new MyChannel("java:comp/env/jms/DealerConnectionFactory",
                                "java:comp/env/jms/ToPlayerDest");
       } catch (Throwable ex) {
          ex.printStackTrace();
       }
    }

Implementing MessageListener means providing an onMessage method:

    public void onMessage(Message msg) {
       // code to process received messages goes here...
    }

Implementing MessageDrivenBean also means providing an ejbRemove method. This method typically deallocates resources allocated by the ejbCreate method:

    public void ejbRemove() {
       try {
           chan.close();
       } catch (Throwable ex) {
          ex.printStackTrace();
       }
    }
Declare an MDB Class

In order for the class created in the previous section to be used as an MDB, it must be declared to be an MDB in the deployment descriptors. This can be done by inserting the following into ejb-jar.xml (as a sub-element of the <enterprise-beans> element):

    <message-driven>
        <ejb-name>DealerEjbName</ejb-name>
        <ejb-class>Dealer</ejb-class>
        ...
    </message-driven>

The <ejb-name> is whatever the Application Component Provider would like to name the MDB. This name will be used later by the Application Assembler and Deployer in order to reference this MDB. The <ejb-class> must be the MDB's fully qualified Java class name.

One more piece of information is still required, and that is who is responsible for managing transactions? If the MDB manages its own transactions, add the following to ejb-jar.xml (as a sub-element of the <message-driven> element):

    <transaction-type>Bean</transaction-type>

If the MDB expects the container to manage transactions, add:

    <transaction-type>Container</transaction-type>

Note:

  • The MDB class can not (successfully) call EJBContext.getUserTransaction unless the transaction type is set to "Bean".
  • Since the MDB's onMessage method is not called until after the inbound message has already been received, the inbound message can not be included in a transaction unless the transaction type is set to "Container".

If for some reason the Application Component Provider feels the MDB should only be used with queues, or only with topics, then the <message-destination-type> element may be used. (This element is an optional subelement of <message-driven> .) If the MDB should only be used with queues, use:

    <message-destination-type>javax.jms.Queue</message-destination-type>

If the MDB should only be used with topics, use:

    <message-destination-type>javax.jms.Topic</message-destination-type>

In many cases the MDB will not care whether the source destination is a queue or a topic, and the <message-destination-type> element may then be omitted.

Finally, when placing the above elements in the ejb-jar.xml file, the correct sequence must be used.

Developing Application Components with JMS: Conclusion

Some key points to note about developing application components:

  • As long as only standard J2EE features are used, the application component is not configured for any specific J2EE provider. (The one catch here is that regardless of whether or not a <res-auth> element in application-client.xml is set to "Container", OC4J does not perform container-managed sign-on for app-clients.)
  • As long as only standard JMS features are used, the application component is not configured:
    • for any specific resource provider,
    • to use (or not use) the JMS Connector,
    • to use any specific administered queues, topics or connection factories, or
    • to impose any JNDI structure on the administered queues, topics or connection factories.
  • When using automatic destination wrapping, the application component indepence described above can be maintained (except for the JNDI structure requirements implicitly imposed by the application component's grouping of JMS destinations) by using environment entries to specifiy the JNDI subcontext(s) used by the application component. (Automatic destination wrapping is not covered in this text and is [by default] not used by the demo, but it is described in the demo files Player.java and oc4j-connectors.xml -- just open each file and search for "Automatic destination wrapping".)

Assembling the Application

The Application Assembler takes application components such as MDBs and app-clients and ties them together. For applications using JMS, the following tasks are typically performed as part of that process:

  1. Declare Message Destinations
  2. Link to Message Destinations
  3. Define the onMessage Transaction Attribute
  4. List the Application Modules

Application Assembler Task #1: Declare Message Destinations

In a typical JMS application, it is common for one application component to send messages to another application component. In order for this to work, JMS requires that the first application component send messages to a JMS Destination, and that the second application compoment receive messages from that same JMS Destination. However, as was seen in the "Declare Logical Names for JMS Resources" task, the Application Component Providers may have chosen any logical names they like, so there is not necessarily any correspondence between the message destination reference names in one application component and another. For example, the application-client (Player) in this demo has a message destination reference named "jms/PlayerResponseDestination" (see application-client.xml), and the MDB (Dealer) in this demo has a message destination reference named "jms/ToPlayerDest" (see ejb-jar.xml). It is the Application Assembler's job to know that messages sent by the the MDB (to "jms/ToPlayerDest") are supposed to be received by the application-client (from "jms/PlayerResponseDestination"). More importantly, the Application Assembler records this knowledge in the deployment descriptors so the Deployer will know to map both of those message destination references to the same JMS Destination.

This section covers the first step of that process - declaring message destinations. The next section will show how to link the message destination references (and MDBs) created by the Application Component Provider to the message destinations created by the Application Assembler. The mapping from logical destinations (message destinations) to physical destinations by the Deployer is covered in the Map Logical Destinations to RA Destinations section.

First, if the ejb-jar.xml does not contain an <assembly-descriptor> element, add one:

    <assembly-descriptor>
        ...
    </assembly-descriptor>

Note: There is no real reason for selecting the ejb-jar.xml file instead of the application-client.xml file (which can also support the elements described in this section). Declaring some message destinations in one file and others in another file is also allowed.

Message destinations are declared using a <message-destination> element, which is a subelement of <assembly-descriptor> . Since the messages in the above example go from the Dealer to the Player, let's create a message destination named "DealerToPlayerMessages":

    <message-destination>
        <message-destination-name>DealerToPlayerMessages</message-destination-name>
    </message-destination>

Note: The message destination name is another type of logical name since message destinations are logical destinations which need to be mapped to physical destinations by the Deployer.

The application-client in this demo also has a message destination reference named "jms/PlayerCommandDestination" (see application-client.xml) which is used to send commands from the player to the dealer. There is no message destination reference for this set of messages in ejb-jar.xml because the MDB class receives these messages without ever looking up a destination (so no logical name is needed). Let's create a message destination named "PlayerToDealerMessages" for this:

    <message-destination>
        <message-destination-name>PlayerToDealerMessages</message-destination-name>
    </message-destination>

Application Assembler Task #2: Link to Message Destinations

The Application Assembler needs to link the message destination references created by the Application Component Providers to the message destinations created in the previous section. This is done by adding <message-destination-link> elements which name the appropriate message destination. For example, in ejb-jar.xml the message destination reference named "jms/ToPlayerDest" was previously declared:

    <message-destination-ref>
        <message-destination-ref-name>jms/ToPlayerDest</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Produces</message-destination-usage>
    </message-destination-ref>

That message destination reference can be linked to the message destination named "DealerToPlayerMessages" by adding a <message-destination-link> element:

    <message-destination-ref>
        <message-destination-ref-name>jms/ToPlayerDest</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Produces</message-destination-usage>
         
                         <message-destination-link>DealerToPlayerMessages</message-destination-link>
    </message-destination-ref>
                      

In application-client.xml we would like to link the message destination reference named "jms/PlayerResponseDestination" to that same message destination. This can be done exactly the same way:

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerResponseDestination</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Consumes</message-destination-usage>
         
                         <message-destination-link>DealerToPlayerMessages</message-destination-link>
    </message-destination-ref>
                      

In a really large application there may be multiple levels of assembly leading to name conflicts (where two different message destinations in two different deployment files have the same name). These conflicts can be resolved by qualifying the message destination name with the name of the deployment file where the message destination is declared, like so:

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerResponseDestination</message-destination-ref-name>
        <message-destination-type>javax.jms.Topic</message-destination-type>
        <message-destination-usage>Consumes</message-destination-usage>
         
                         <message-destination-link>dealer-ejb.jar#DealerToPlayerMessages</message-destination-link>
    </message-destination-ref>
                      

The last unlinked message destination reference (named "jms/PlayerCommandDestination") is also in application-client.xml and should be linked to the message destination named "PlayerToDealerMessages":

    <message-destination-ref>
        <message-destination-ref-name>jms/PlayerCommandDestination</message-destination-ref-name>
        <message-destination-type>javax.jms.Queue</message-destination-type>
        <message-destination-usage>Produces</message-destination-usage>
         
                         <message-destination-link>PlayerToDealerMessages</message-destination-link>
    </message-destination-ref>
                      

The very last message destination link that is needed by this demo is for the MDB inbound messaging. There is no message destination reference for MDB inbound messaging, so the <message-destination-link> element is a direct subelement of the <message-driven> element in ejb-jar.xml:

    <message-driven>
        ...
         
                         <message-destination-link>PlayerToDealerMessages</message-destination-link>
                      

The <message-destination-link> element must be placed in the correct sequence with respect to the other subelements of <message-driven> .

Application Assembler Task #3: Define the onMessage Transaction Attribute

If the MDB's transaction type has been set to "Container", then a transaction attribute should be defined for the MDB's onMessage method.

Although this is listed as an Application Assembler task, it may be performed by the Application Component Provider or by the Deployer.

To define the transaction attribute for the onMessage method of the MDB named "DealerEjbName", add the following to ejb-jar.xml as a subelement of <assembly-descriptor> :

    <container-transaction>
        <method>
            <ejb-name>DealerEjbName</ejb-name>
            <method-name>onMessage</method-name>
        </method>
        <trans-attribute>Required</trans-attribute>
    </container-transaction>

(There are some other transaction options defined by the EJB spec. They are not covered in this demo because if you need to use those options with your MDBs, you're probably doing something wrong.)

There is an ordering requirement rooted in the EJB schema file. As per that document, the correct ordering for the sub-elements of <assembly-descriptor> used by this demo is:

  1. <container-transaction> element(s)
  2. <message-destination> element(s)

Application Assembler Task #4: List the Application Modules

The application .ear file may contain a number of jar files (whatever their actual file-extension may be). In order for the application server to know what it should do with each of the jar files (if anything), each module (each jar file containing one or more application components) must be listed in the application.xml file using a <module> element.

Jar files containing EJBs (like the MDB included in this demo) should use the <ejb> module type:

    <module>
        <ejb>dealer-ejb.jar</ejb>
    </module>

Jar files containing an application-client should use the <java> module type:

    <module>
        <java>player-client.jar</java>
    </module>

Jar files containing a resource adapter (such as the JMS Connector) should use the <connector> module type:

    <module>
        <connector>TibcoJMSRADeploy.rar</connector>
    </module>

Note that one of this demo's jar files, common.jar, is not listed as a module. This is because it does not contain any application components, just some classes that are used by both the application-client and the MDB. So instead of being listed in the application.xml file, common.jar is listed in the Class-Path in the MANIFEST.MF files for player-client.jar and dealer-ejb.jar.

Configuring the Resource Provider

  1. Create RP Connection Factories
  2. Create RP Destinations
  3. Declare a Resource Provider Reference

Resource Provider Task #1: Create RP Connection Factories

For real applications...

The person responsible for administering the resource provider might or might not have to create connection factories specifically for a given application. If need be, the type of RP connection factories required by the application can be determined by inspecting the application's deployment descriptors, specifically the <resource-ref> elements created by the Application Component Provider(s). Whether or not an application actually requires application-specific RP connection factories (or can just use the same connection factories as one or more other applications) is both application and resource-provider specific, as is the specific configuration such connection factories may require. There is no J2EE standard method for conveying this information.

For this demo...

The RP must provide the following connection factories:

This is because all of the JMS Connector and application configuration described after this point has already been performed for you using those RP connection factory JNDI names. However, you can use whatever RP connection factory JNDI names you like as long as you perform the configuration processes described after this point using the JNDI names you have selected.

* Note: These JNDI names are the JNDI locations of the RP connection factories within the resource provider's JNDI context. Access to that JNDI context is acheived by declaring a resource provider reference.

Please refer to the Tibco Enterprise for JMS documentation or speak with your Tibco Enterprise for JMS administrator for information on how to configure Tibco Enterprise for JMS connection factories.

The demo application only uses TibcoCF (for the Application Client) and TibcoXACF (for the MDB). However, examples of all 6 types of JMS connection factories are included in the JMS Connector configuration.

Resource Provider Task #2: Create RP Destinations

For real applications...

The person responsible for administering the resource provider may have to determine the number and type of RP destinations required by the application by inspecting the application's deployment descriptors. The Application Assembler determined that two destinations are required, and recorded that information into the application component deployment descriptors by declaring exactly two <message-destination> s. The <message-destination-link> s left by the Application Assembler can be followed back to the <message-destination-ref> elements created by the Application Component Provider(s), and their <message-destination-type> subelements show that one of those destinations should be a queue and one should be a topic.

Alternatively, someone else (e.g., the deployer, the person responsible for configuring the JMS Connector, the application assembler, etc) may provide information about the required RP destinations via some other mechanism (not covered by the J2EE standard).

For this demo...

The RP must provide the following destinations:

This is because all of the JMS Connector and application configuration described after this point has already been performed for you using those RP destination names. However, you can use whatever RP destination names you like as long as you perform the configuration processes described after this point using the names you have selected.

* Note: These JNDI names are the JNDI locations of the RP destinations within the resource provider's JNDI context. Access to that JNDI context is acheived by declaring a resource provider reference.

Please refer to the Tibco Enterprise for JMS documentation or speak with your Tibco Enterprise for JMS administrator for information on how to configure Tibco Enterprise for JMS destinations.

Resource Provider Task #3: Declare a Resource Provider Reference

Before an application (or JMS Connector) running in OC4J can access a resource provider, a resource provider reference must be declared with the <resource_provider> element. The resource provider reference holds miscellaneous data (e.g., the class name described below) that OC4J uses to interact with the resource provider. The resource provider reference also provides a JNDI subcontext through which resource provider resources can be accessed. The resource provider reference (and said JNDI access) can be made local to the application (by placing it in orion-application.xml), or available to all applications (by placing it in %ORACLE_HOME%/j2ee/home/config/application.xml). This demo uses a local resource provider reference.

The two pieces of information you must provide whenever declaring a resource provider reference are the name you wish to use for the resource provider reference and the Java class which implements the resource provider interface. For Tibco Enterprise for JMS the class is com.evermind.server.deployment.ContextScanningResourceProvider. To declare a resource provider reference named TibcoJMSReference, use:

    <resource-provider class="com.evermind.server.deployment.ContextScanningResourceProvider" name="TibcoJMSReference">
    ...
    </resource-provider>

The resource provider reference maps the resource provider's JNDI context (which contains RP connection factories and RP destinations) to a JNDI subcontext accessible by the application and, more importantly, the JMS Connector. That JNDI subcontext is java:comp/resource/ name where " name" is the name of the resource provider reference. For example, if the resource provider reference is named TibcoJMSReference, and the JNDI name of a resource provider queue is TibcoQ, then that RP queue is accessible to the application and JMS Connector at JNDI location java:comp/resource/TibcoJMSReference/TibcoQ. The reason it is more important for the JMS Connector to be able to access RP resources than for the application to be able to do so is that, when using the JMS Connector, the application need not (and in general should not) directly look up or use any RP resources.

For Tibco Enterprise for JMS the following properties must be provided in the <resource-provider> element:

  • "java.naming.factory.initial" - For Tibco Enterprise for JMS this is always "com.tibco.tibjms.naming.TibjmsInitialContextFactory".
  • "java.naming.provider.url" - The correct value of this property depends on how Tibco Enterprise for JMS has been administered.

For me the final result looks like:

    <resource-provider class="com.evermind.server.deployment.ContextScanningResourceProvider" name="TibcoJMSReference">
         
                         <property              name="java.naming.factory.initial"              value="com.tibco.tibjms.naming.TibjmsInitialContextFactory">         </property>         <property              name="java.naming.provider.url"              value="tibjmsnaming://jleinawe-sun:7222">         </property>
    </resource-provider>
                      

Note: You will need to modify the "java.naming.provider.url" value in the orion-application.xml file to match how Tibco Enterprise for JMS has been administered. See the Tibco Enterprise for JMS documentation or speak with your Tibco Enterprise for JMS administrator to determine the correct values.

Tibco Enterprise for JMS is implemented via jar files which must be in the application's class-path. There are various ways to do this, but the cleanest in this case (since the resource provider reference is already in the orion-application.xml file) is to add a <libary> element to orion-application.xml:

    <library path="/home/jsteidl/lib/tibco_jars"/>

Note: You will need to modify the above path in the orion-application.xml file to point to the directory where the Tibco Enterprise for JMS jar files are located. See the Tibco Enterprise for JMS documentation or speak with your Tibco Enterprise for JMS administrator for the list of required jar files and where to find them.

Configuring the JMS Connector

Configuring Oracle's JMS Connector typically includes the following tasks:

  1. Customize the ra.xml File
  2. Create a JMS Connector Instance
  3. Create RA Connection Factories
  4. Create RA Destinations

Oracle's JMS Connector is a Resource Adapter (RA). The RA configuration files included with this demo are provided "ready to run". This section is included anyways, to provide information that will be useful when you want to perform your own JMS Connector configuration for real applications. (For brevity, this document refers to JMS Connector connection factories and destiations as "RA connection factories" and "RA destinations".)

Things to keep in mind...

The JMS Connector acts as a "value added" wrapper for the resource provider:

  • The work of sending and receiving messages is done by the resource provider. (For example, when you call a JMS Connector MessageProducer to send a message, the JMS Connector then calls the resource provider to perform the send.) The JMS Connector "adds value" to these operations such as lazy enlistment and statistics collection. (For example, before calling the resource provider to perform the send, the JMS Connector may enlist the resource provider into a global transaction.)
  • The physical connection on which messages are sent and received is provided by the resource provider. (For example, when you call a JMS Connector connection factory to create a connection, the JMS Connector then calls the resource provider to obtain access to a physical connection.) The JMS Connector (acting in concert with the application server) "adds value" to this operation such as connection pooling and container-managed sign-on. (For example, unused resource provider connections can be "pooled". When you call a JMS Connector connection factory to create a connection the JMS Connector may get a resource provider connection from the "pool" rather than asking the resource provider to create a new connection.)

The JMS Connector also acts as a compatability layer for the resource provider, allowing things like container-managed transactions and MDBs. Specific instances of the JMS Connector based on Oracle's JMS Connector implementation can be configured for a number of different resource providers, including Tibco Enterprise for JMS.

JMS Connector Task #1: Customize the ra.xml File

The ra.xml file contains a lot of content that does not need to (and should not) be changed when using Oracle's JMS Connector. (The reason for this is that the ra.xml file is based on the J2EE Connector Architecture 1.5 schema file, which is a generic schema intended to work with many types of resource adapters, including non-JMS resource adapters, and other JMS resource adapter implementations.)

The ra.xml file included with this demo can be quickly modified to work with any of the supported resource providers by making the following changes:

  • Set <display-name> as desired.
  • Set <eis-type> as desired.
  • Set the "resourceProviderName" properties to the resource provider reference name. (See the Declare a Resource Provider Reference section.)
  • Set the "jndiLocation" properties to the JNDI names of default RP connection factories. (See the Create RP Connection Factories section.)
  • Set the "jndiName" properties to the JNDI names of some default/representative queue and topic. (See the Create RP Destinations section.)

The ra.xml file included with this demo contains inline documentation which describes the file's contents.

JMS Connector Task #2: Create a JMS Connector Instance

Similar to how a resource provider reference was needed to interact with the resource provider, a JMS Connector instance is needed to interact with the JMS Connector. A JMS Connector instance is created with the <connector> element (a subelement of the <oc4j-connectors> element in the oc4j-connectors.xml file).

The two pieces of information you must provide whenever creating a JMS Connector instance are:

  • "name" - the name you wish to use for the JMS Connector instance (and where in JNDI you would like the JMS Connector instance to be bound), and
  • "path" - where the resource adapter module (.rar file) is located.
oc4j-connectors.xml
    <connector name="TibcoJMSRAInstanceName" path="TibcoJMSRADeploy.rar">
    ...
    </connector>

The JMS Connector instance can be made local to the application by:

  • including the resource adapter module in the application's .ear file and declaring it as a <connector> <module> in the application.xml file (so it is automatically deployed when the application is deployed),
  • placing the <connector> element in the application's oc4j-connectors.xml file, and
  • pointing to the oc4j-connectors.xml file by placing a <connectors> element in the application's orion-application.xml file
  • deploying the resource adapter module (e.g., using admin.jar's "-deployconnector" command),
  • placing the <connector> element in the %ORACLE_HOME%/j2ee/home/config/oc4j-connectors.xml.xml file, and
  • pointing to the %ORACLE_HOME%/j2ee/home/config/oc4j-connectors.xml.xml file by placing a <connectors> element in the %ORACLE_HOME%/j2ee/home/config/application.xml file

The <connectors> element used to point to the oc4j-connectors.xml file is a subelement of the <orion-application> element and should look something like this:

    <connectors path="oc4j-connectors.xml"/>

JMS Connector Task #3: Create RA Connection Factories

For real applications...

Many of the same considerations for RP connection factores also apply to RA connection factories.

Some level of cooperation between the person responsible for configuring the JMS Connector and the person responsible for administering the resource provider may be required in order to know which RP connection factories should be wrapped by which RA connection factories.

RA resources (RA connection factories and RA destinations) are either placed at the root of JNDI or within some JNDI subcontext. Any number of JNDI subcontexts may be used, where one or more RA resources are placed in each JNDI subcontext. When the RA is deployed locally (i.e., no other applications will be able to see the RA's resources in their JNDI context), the JNDI organization scheme is not critical. When an RA is deployed independently and RA resources are made visible to all applications, more consideration needs to be given to this issue. Like using subdirectories on a hard-drive to keep things organized, you should use subcontexts to keep JNDI organized. One scheme, for example, would be to use a JNDI subcontext like ApplicationName/JMSRA where ApplicationName is the name of the application which will be using the RA resources. If the RA connection factories are generic and can be used by multiple applications, then they could be placed somewhere like JMSRA (and leave the RA destinations created for specific applications in application-specific JNDI subcontexts). If two applications communicate via one or more RA destinations, then those destinations could be placed in their own JNDI subcontext.

For this demo...

All of the RA resources are in the JNDI subcontext TibcoJMSRASubcontext. Since the RA is deployed locally, this is a sufficient organization strategy.

The RA must provide the following connection factories:

This is because all of the RA and application configuration described after this point (including creating the above RA connection factories) has already been performed for you using those JNDI locations. However, you can use whatever (otherwise unused) JNDI locations you like as long as you perform the configuration processes described after this point using the JNDI locations you have selected.

The demo application only uses the two RA connection factories listed above. However, in order to provide examples of all 6 types of JMS connection factories the following RA connection factories are also created in oc4j-ra.xml:

How to Create RA Connection Factories...

To create an RA connection factory at JNDI location TibcoJMSRASubcontext/MyCF, insert the following into oc4j-ra.xml (as a sub-element of the <oc4j-connector-factories> element):

    <connector-factory location="TibcoJMSRASubcontext/MyCF" ... >
        ...
    </connector-factory>

To indicate what type of RA connection factory should be created, add a <connectionfactory-interface> element:

    <connector-factory location="TibcoJMSRASubcontext/MyCF" ... >
        ...
         
                         <connectionfactory-interface>javax.jms.ConnectionFactory</connectionfactory-interface>
    </connector-factory>
                      

The value of the <connectionfactory-interface> element must match one of the <connectionfactory-interface> values defined in the ra.xml file.

As described previously, the RA connection factory makes use of the RP connection factory. Before it can do that, you must tell the RA connection factory where it can find an appropriate RP connection factory. To tell this RA connection factory to use the RP connection factory located in the resource provider's JNDI context at TibcoCF, add a <config-property> element which sets the "jndiLocation" property:

    <connector-factory location="TibcoJMSRASubcontext/MyCF" ... >
         
                         <config-property name="jndiLocation" value="TibcoCF"/>
        <connectionfactory-interface>javax.jms.ConnectionFactory</connectionfactory-interface>
    </connector-factory>
                      

Note: An "appropriate" RP connection factory is one that is compatible with the <connectionfactory-interface> . In the case of oracle.j2ee.ra.jms.generic.XABackedConnectionFactory the RP connection factory must implement javax.jms.XAConnectionFactory. In all other cases the RP connection factory must implement the interface defined by <connectionfactory-interface> .

If the "jndiLocation" property is not set here (i.e., the <config-property> element is ommitted), then the default "jndiLocation" for this <connectionfactory-interface> is used, as set in ra.xml.

Finally, indicate the RA instance with which this RA connection factory will be used. (This must match the "name" attribute of the <connector> element which was set previously.)

    <connector-factory location="TibcoJMSRASubcontext/MyCF"  
                         connector-name="TibcoJMSRAInstanceName">
        <config-property name="jndiLocation" value="TibcoCF"/>
        <connectionfactory-interface>javax.jms.ConnectionFactory</connectionfactory-interface>
    </connector-factory>
                      

The above <connector-factory> settings are (just about) the bare minimum required by the schema file. Real applications may include a number of other subelements pertaining to features such as container-managed sign-on and connection pooling. See "Oracle Containers for J2EE: Resource Adapter Administrator's Guide" for complete coverage of <connector-factory> settings.

JMS Connector Task #4: Create RA Destinations

For real applications...

The person responsible for configuring the JMS Connector will need to determine or be told the number and type of RA destinations required. Some level of cooperation between the person responsible for configuring the JMS Connector and the person responsible for administering the resource provider may be required in order to know which RP destiations should be wrapped by which RA destinations.

Alternatively, automatic destination wrapping may be used. This allows any number of RP destinations to be accessed via as few as two RA destinations (one for queues and one for topics). (Automatic destination wrapping is not covered in this text and is [by default] not used by the demo, but it is described in the demo files Player.java and oc4j-connectors.xml -- just open each file and search for "automatic destination wrapping".)

For this demo...

The RA must provide the following destinations:

This is because all of the JMS Connector and application configuration described after this point (including creating the above RA destinations) has already been performed for you using those JNDI locations. However, you can use whatever JNDI locations you like as long as you perform the configuration processes described after this point using the JNDI locations you have selected.

How to create RA Destinations...

To create an RA destination at JNDI location TibcoJMSRASubcontext/MyQ, insert the following into oc4j-connectors.xml (as a sub-element of the <connector> element created previously):

    <adminobject-config location="TibcoJMSRASubcontext/MyQ">
        ...
    </adminobject-config>

To indicate what type of RA destination should be created, add an <adminobject-class> element:

    <adminobject-config location="TibcoJMSRASubcontext/MyQ">
         
                         <adminobject-class>oracle.j2ee.ra.jms.generic.AdminObjectQueueImpl</adminobject-class>
        ...
    </adminobject-config>
                      

The value of the <adminobject-class> element must be either oracle.j2ee.ra.jms.generic.AdminObjectQueueImpl (to create a queue) or oracle.j2ee.ra.jms.generic.AdminObjectTopicImpl (to create a topic).

Just as RA connection factories act as wrappers for RP connection factories, RA destinations act as wrappers for RP destinations. In order for the RA to look up the RP destination, the RA needs the name of the resource provider reference and the RP's JNDI name for the RP destination (the JNDI location of the RP destination within the resource provider's JNDI context):

    <adminobject-config location="TibcoJMSRASubcontext/MyQ">
        <adminobject-class>oracle.j2ee.ra.jms.generic.AdminObjectQueueImpl</adminobject-class>
         
                         <config-property name="jndiName" value="TibcoQ"/>         <config-property name="resourceProviderName" value="TibcoJMSReference"/>
    </adminobject-config>
                      

Configuring the Application for Deployment

The Deployer takes J2EE application and adds configuration information which may be specific to an app-server provider as well as to a specific deployment. For applications using JMS, the following tasks are typically performed as part of that process:

  1. Map Logical Connection Factories to RA Connection Factories
  2. Map Logical Destinations to RA Destinations
  3. Configure the MDB
  4. Configure the App-Client Properties
  5. Configure the Class-Path

Deployer Task #1: Map Logical Connection Factories to RA Connection Factories

As covered previously, the app-client component declares a logical name for a connection factory in the application-client.xml file:

    <resource-ref>
        <res-ref-name>jms/PlayerConnectionFactory</res-ref-name>
        <res-type>javax.jms.ConnectionFactory</res-type>
        <res-auth>Application</res-auth>
    </resource-ref>

The Deployer maps that logical name ( jms/PlayerConnectionFactory) to an RA connection factory which implements the javax.jms.ConnectionFactory interface and is located in JNDI at TibcoJMSRASubcontext/MyCF:

    <resource-ref-mapping
        location = "TibcoJMSRASubcontext/MyCF"
        name = "jms/PlayerConnectionFactory"/>

The above mapping is in the orion-application-client.xml file.

Similarly, in the orion-ejb-jar.xml file the Deployer maps the logical name jms/DealerConnectionFactory to the RA XA connection factory located in JNDI at TibcoJMSRASubcontext/MyXACF:

    <resource-ref-mapping
        location = "TibcoJMSRASubcontext/MyXACF"
        name = "jms/DealerConnectionFactory"/>

The app-client was non-transactional, so it was given a non-XA connection factory. The MDB is transactional (it has a <trans-attribute> of Required), so it requires an XA connection factory.

Note: Sending and receiving messages using producers/consumers derived from a non-XA connection factory will not work within a global transaction. Likewise, sending and receiving messages using producers/consumers derived from an XA connection factory will not work outside of a global transaction.

Deployer Task #2: Map Logical Destinations to RA Destinations

As described in the Link to Message Destinations task, the Application Assembler has provided the necessary information about which sets of message destination references should be mapped to a single physical JMS Destination. For each <message-destination> , the Deployer must map all <message-destination-ref> s and MDBs linked to that <message-destination> to the same destination.

For example, in ejb-jar.xml a <message-destination> named DealerToPlayerMessages is declared. In file application-client.xml the <message-destination-ref> named jms/PlayerResponseDestination is linked to that <message-destination> , and in file ejb-jar.xml the <message-destination-ref> named jms/ToPlayerDest is also linked to that <message-destination> . Note that the <message-destination-type> for both of these is javax.jms.Topic. The Deployer maps both of these <message-destination-ref> s to an RA topic at JNDI location TibcoJMSRASubcontext/MyT by adding this to the orion-application.xml file:

    <message-destination-ref-mapping
        location = "TibcoJMSRASubcontext/MyT"
        name = "jms/PlayerResponseDestination"/>

and this to the orion-ejb-jar.xml file:

    <message-destination-ref-mapping
        location = "TibcoJMSRASubcontext/MyT"
        name = "jms/ToPlayerDest"/>

Note: In 10.1.3 DP4 this was required. However, in 10g R3 the trail from <message-destination-ref> to <message-destination-link> to <message-destination> to <message-destination-mapping> is followed automatically for EJBs. So the above <message-destination-ref-mapping> is not actually required, and the orion-ejb-jar.xml file for this demo does not include it.

Similarly, in ejb-jar.xml a <message-destination> named PlayerToDealerMessages is declared. In file application-client.xml the <message-destination-ref> named jms/PlayerCommandDestination is linked to that <message-destination> , and in file ejb-jar.xml the MDB is also linked to that <message-destination> . The Deployer maps both of these to an RA queue at JNDI location TibcoJMSRASubcontext/MyQ. The <message-destination-ref> is handled by adding another <message-destination-ref-mapping> to the orion-application.xml file:

    <message-destination-ref-mapping
        location = "TibcoJMSRASubcontext/MyQ"
        name = "jms/PlayerCommandDestination"/>

Mapping the MDB to a physical destination is done as part of the MDB's activation specification (described more in the next section). The Deployer sets the DestinationName property to TibcoJMSRASubcontext/MyQ in the orion-ejb-jar.xml file:

    <config-property>
        <config-property-name>DestinationName
        </config-property-name>
        <config-property-value>TibcoJMSRASubcontext/MyQ
        </config-property-value>
    </config-property>

Deployer Task #3: Configure the MDB

To configure an MDB for deployment, add a <message-driven-deployment> element to the orion-ejb-jar.xml file as a subelement of <enterprise-beans> :

    <enterprise-beans>
        ...
        <message-driven-deployment
            name="DealerEjbName"
            ... >
           ...
        </message-driven-deployment>

Note that the "name" attribute must be set to the same value as the <ejb-name> element for the given MDB in the ejb-jar.xml file.

In order for the MDB to be activated by messages received by the JMS Connector, the MDB and JMS Connector must be connected. This is done by setting the "resource-adapter" attribute equal to the name of the JMS Connector instance:

    <message-driven-deployment 
        name="DealerEjbName"
         
                         resource-adapter="TibcoJMSRAInstanceName">
       ...
    </message-driven-deployment>
                      

When the MDB is deployed, the application server provides the MDB's activation specification to the JMS Connector. The activation specification is a collection of properties which may be defined in the ejb-jar.xml file via <activation-config-property> elements:

    <activation-config-property>
        <activation-config-property-name>ExamplePropertyName
        </activation-config-property-name>
        <activation-config-property-value>ExampleValue
        </activation-config-property-value>
    </activation-config-property>
orion-ejb-jar.xml <config-property>
    <config-property>
        <config-property-name>ExamplePropertyName
        </config-property-name>
        <config-property-value>ExampleValue
        </config-property-value>
    </config-property>

Since activation specification properties are specific to a given resource adapter, using the app-server-specific orion-ejb-jar.xml file is the recommended approach.

One of the activation specification properties ( DestinationName) was already covered in the previous section. When messages are sent to the destination defined by that property (and are not filtered by any MessageSelector property), the messages are then delivered to the MDB. DestinationName is a required property.

Another required property is ConnectionFactoryJndiName. This defines the connection factory the JMS Connector should use to create a connection (which it then uses to retreive messages from the above destination on behalf of the MDB). Since the MDB in this demo is transactional (it has a <trans-attribute> of Required), the JMS Connector needs an XA connection factory and the ConnectionFactoryJndiName property is set to TibcoJMSRASubcontext/MyXACF.

The last property which is always required is DestinationType. When using a resource provider which implements JMS 1.0.2b (and not JMS 1.1), it must be set to javax.jms.Queue if the destination is a queue and javax.jms.Topic if the destination is a topic. When using a resource provider which implements JMS 1.1 it may also be set to javax.jms.Destination (for either a queue or a topic).

This demo only requires the activation specification properties described above. However, the orion-ejb-jar.xml file included with this demo contains documentation for all of the properties supported by Oracle's JMS Connector implementation.

Deployer Task #4: Configure the App-Client Properties

The jndi.properties file is typically used to control what happens when an application client creates and uses a javax.naming.InitialContext. The

  • "java.naming.factory.initial" - always set to com.evermind.server.ApplicationClientInitialContextFactory
  • "java.naming.provider.url" - ORMI address of the server application context (where InitialContext lookups should be handled)
  • "java.naming.security.principal" - user (must have RMI login permission)
  • "java.naming.security.credentials" - password

Note that these not the same "java.naming.factory.initial" and "java.naming.provider.url" properties discussed in the Declare a Resource Provider Reference section. In that section those properties were used to control what happens when the com.evermind.server.deployment.ContextScanningResourceProvider class creates and uses an javax.naming.InitialContext.

See the Configuring the Environment section for more on the format and content of the jndi.properties file.

Deployer Task #5: Configure Class Paths

There are several ways to manipulate the class search path. This demo uses the "Class-Path" section of the MANIFEST.MF files. (There is one MANIFEST.MF file for each application component, as well as a top-level MANIFEST.MF for the application.) This demo also uses the <library> element discussed previously.

Often, the class path needs of application components other than application clients are satisfied by the class paths provided by default. However, this demo contains some classes which are used by both the application client and the MDB. Those classes have been placed in their own jar file ( common.jar). In order to allow those application components to locate those common classes, common.jar is listed in the "Class-Path" section of the MANIFEST.MF files for both the application client and the MDB. Since this dependency is known at the Application Assembler level, it may be configured there rather than by the Deployer.

The application client does not get the same default class paths as other application components. This means that a number of app-server and J2EE-standard jar files need to be included in the "Class-Path" section of the MANIFEST.MF file for the application client. (A brute-force solution is to simply include every jar file in %ORACLE_HOME%/j2ee/home and %ORACLE_HOME%/j2ee/home/lib in addition to any required 3rd party jar files.)

Running the Application

The following instructions are for running this demonstration on a standalone or managed instance of Oracle Containers for J2EE 10g Release 3.

Examining the How to Distribution

  • build - temporary directory created during the build
  • log - temporary directory holding deploy/undeploy logs
  • dist - temporary directory holding the built application (ear file)
  • doc - the How-to document and Javadoc's
    • javadoc - the javadoc of the different source files
    • how-to-connect-to-tibco.html - this How-to page
  • src - the source for the demo
    • ejb - contains the MDB (dealer) Java code and configuration files
    • client - contains the app-client (player) Java code and configuration files
    • common - contains Java code used by both the MDB and app-client
    • ra - contains the RA configuration files
    • META-INF - top-level configuration files for application

Configuring the Environment

Make sure the following environment variables are defined:

  • %ORACLE_HOME% - The directory where you installed OC4J.
  • %JAVA_HOME% - The directory where your JDK is installed.

Please check to make sure that the following properties are configured correctly in the ant-oracle.properties file located in the root of the sample's distribution (NOTE: Some of these properties will default to the values of corresponding environment variables as noted below. If you have these variables set up in your environment you will not have to alter the values in the file unless ant is unable to access environment variables on your platform). Modify these variables as needed for your environment:

  • oracle.home - the root directory of oracle installation. Defaults to ORACLE_HOME env variable.
  • java.home - the root directory of JDK installation. Defaults to JAVA_HOME env variable.
  • oracleas.host - the hostname of the platform on which the OC4J instance is running. Defaults to localhost.
  • oracleas.admin.port - the port on which the OC4J administration processor is listening. Defaults to 23791.
  • oracleas.admin.user - the name of the OC4J administrator. Defaults to "oc4jadmin".
  • oracleas.admin.password - the password for the OC4J administrator. Defaults to "welcome".

If oracleas.host, oracleas.admin.port, oracleas.admin.user or oracleas.admin.password is changed, update %HOWTO_HOME%/src/client/jndi.properties as well. That file should have the following format:

    java.naming.factory.initial=com.evermind.server.ApplicationClientInitialContextFactory
    java.naming.provider.url=ormi://${oracleas.host}:${oracleas.admin.port}/how-to-connect-to-tibco
    java.naming.security.principal=${oracleas.admin.user}
    java.naming.security.credentials=${oracleas.admin.password}

If you are running a managed version of Oracle Application Server 10g, you must change the following values to match your configuration:

  • oracleas.deployer.uri - the URI used to perform administrative operations (deploy, undeploy). The ant-oracle.properties file contains three different URIs, one for each of the following topologies: stand alone OC4J, Managed Single Node or Managed Cluster. Uncomment the URI which corresponds to your toplogy. Defaults to stand alone OC4J.
  • oracleas.oc4j.instance - the OC4J instance name. Defaults to "home".

The properties in the ant-oracle.properties file which are not listed above are not used by this demo and can be ignored.

Finally, please make sure that the ant command associated with the OC4J ant distribution is in your execution path ( %OC4J_HOME%/ant/bin).

Starting the OC4J Instance

Start an OC4J 10.1.3 instance as follows:

  • For an OC4J stand alone installation:
        %ORACLE_HOME%/bin/oc4j start
    

    Note that the oc4j command expects the JAVA_HOME environment variable to point to a full JDK installation.

  • For an OracleAS managed installation:
        %ORACLE_HOME%/opmn/bin/opmnctl startall
    

Generating, Compiling and Deploying the Application

To build the application, type the following command from the %HOWTO_HOME% directory:

    ant

You should now have the newly created how-to-connect-to-tibco.ear in your %HOWTO_HOME%/dist directory.

This command will also attempt to deploy the application if the build is successful. It will first test whether OC4J is running.

Note that you can also deploy the application separately. Ensure the %ORACLE_HOME% environment variable is defined, and from the %HOWTO_HOME% directory, type the command:

    ant deploy

Running the Application

From three terminal windows (all with environment entries set as described above), go to the %HOWTO_HOME% directory and start a player. There are four players available, and each can be started with an ant command:

    ant larry
    ant curly
    ant moe
    ant joe

For example, do ant larry from the first terminal, ant curly from the second, and ant moe from the third.

The three app-client players will join the game run by the MDB dealer and play the game described in the Introduction.

Summary

You have learned:

  • How to develop, assemble and configure a JMS application.
  • How to configure Oracle's JMS Connector implementation for Tibco Enterprise for JMS.
Object Type Use During Demo Initialization   Object Type Use After Demo Initialization
javax.naming.Context look up ConnectionFactory and Destination   javax.naming.Context (none)
javax.jms.ConnectionFactory create Connection   javax.jms.ConnectionFactory (none)
javax.jms.Connection create Session   javax.jms.Connection close everything
javax.jms.Destination create MessageProducer and MessageConsumer   javax.jms.Destination (none)
javax.jms.Session create MessageProducer and MessageConsumer   javax.jms.Session create messages
javax.jms.MessageProducer (none)   javax.jms.MessageProducer send messages
javax.jms.MessageConsumer (none)   javax.jms.MessageConsumer receive messages
JNDI Name * Java interface
TibcoCF javax.jms.ConnectionFactory
TibcoQCF javax.jms.QueueConnectionFactory
TibcoTCF javax.jms.TopicConnectionFactory
TibcoXACF javax.jms.XAConnectionFactory
TibcoXAQCF javax.jms.XAQueueConnectionFactory
TibcoXATCF javax.jms.XATopicConnectionFactory
JNDI Name * Java interface
TibcoQ javax.jms.Queue
TibcoT javax.jms.Topic
JNDI Location Java interface
TibcoJMSRASubcontext/MyCF javax.jms.ConnectionFactory
TibcoJMSRASubcontext/MyXACF oracle.j2ee.ra.jms.generic.XABackedConnectionFactory
JNDI Location Java interface
TibcoJMSRASubcontext/MyQCF javax.jms.QueueConnectionFactory
TibcoJMSRASubcontext/MyTCF javax.jms.TopicConnectionFactory
TibcoJMSRASubcontext/MyXAQCF javax.jms.XAQueueConnectionFactory
TibcoJMSRASubcontext/MyXATCF javax.jms.XATopicConnectionFactory
JNDI Location Java interface
TibcoJMSRASubcontext/MyQ javax.jms.Queue
TibcoJMSRASubcontext/MyT javax.jms.Topic
Left Curve
Popular Downloads
Right Curve
Untitled Document