Middleware
Application Server
Date: 02/02/06
Author: Jeff Steidl
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.
This demo requires that the following sofware components are installed and configured correctly:
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:
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.
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:
This section concludes with some key points concerning the development of application components.
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 ).
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 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:
Also, real code may not limit the above activities to "initialization" code. For example:
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:
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:
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:
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/".
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:
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:
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.
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:
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:
chan = new MyChannel("java:comp/env/jms/DealerConnectionFactory",
"java:comp/env/jms/ToPlayerDest");
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 .
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();
}
}
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:
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.
Some key points to note about developing application components:
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:
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>
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> .
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:
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.
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.
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.
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).
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.
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:
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 Oracle's JMS Connector typically includes the following tasks:
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".)
The JMS Connector acts as a "value added" wrapper for the resource provider:
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.
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:
The ra.xml file included with this demo contains inline documentation which describes the file's contents.
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:
<connector name="TibcoJMSRAInstanceName" path="TibcoJMSRADeploy.rar">
...
</connector>
The JMS Connector instance can be made local to the application by:
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"/>
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.
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:
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.
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".)
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.
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>
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:
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.
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>
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.
The jndi.properties file is typically used to control what happens when an application client creates and uses a javax.naming.InitialContext. The
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.
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.)
The following instructions are for running this demonstration on a standalone or managed instance of Oracle Containers for J2EE 10g Release 3.
Make sure the following environment variables are defined:
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:
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:
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).
Start an OC4J 10.1.3 instance as follows:
%ORACLE_HOME%/bin/oc4j start
Note that the oc4j command expects the JAVA_HOME environment variable to point to a full JDK installation.
%ORACLE_HOME%/opmn/bin/opmnctl startall
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
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.
You have learned:
| 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 |