This document and accompanying demo application show how to configure Oracle's JMS Connector
implementation for OEMS JMS (Database). 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 document uses ellipses inside of an xml block:
<some-element>
...
</some-element>
to indicate that the XML example is incomplete. Complete versions of such examples are always
provided later on in the same section as the incomplete example.
Ellipses may also be used to indicate the context of an XML block:
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 OEMS JMS (Database).
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:
Writing code that sends and receives JMS messages requires using the following types of objects:
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
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:
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.
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):
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:
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:
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:
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):
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:
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:
To tell the Deployer (and Application Assembler) that the application component both consumes
messages from and produces message to the destination, add:
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:
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:
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:
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:
jms/PlayerConnectionFactory
jms/PlayerCommandDestination
jms/PlayerResponseDestination
it must use the following JNDI locations:
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:
MyChannel command = new MyChannel(factoryName, commandDestName);
MyChannel response = new MyChannel(factoryName, responseDestName);
The MyChannel constructor uses the passed-in JNDI locations to
perform JNDI lookups to retrieve the connection factory and destination.
(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:
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 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:
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:
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):
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.
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:
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:
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".)
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":
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:
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:
That message destination reference can be linked to the message destination named
"DealerToPlayerMessages" by adding a <message-destination-link> element:
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:
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:
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":
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:
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>:
(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:
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.
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).
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.
Enter the password for the system dba account when prompted. Then execute the oemsjmsd.sql script:
@oemsjmsd.sql
Note that if oemsjmsd.sql is not in the current directory, the full path to the file should be
given in the above step.
The oemsjmsd.sql script creates a table named QTab, and then uses that table to create
(and start) a queue named MY_QUEUE (which OEMS JMS (Database) calls Queues/MY_QUEUE for JNDI purposes):
The oemsjmsd.sql script also creates a table named TTab, and then uses that table to
create (and start) a topic named MY_TOPIC (which OEMS JMS (Database) calls Topics/MY_TOPIC for JNDI
purposes):
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 OEMS JMS (Database) the class is
oracle.jms.OjmsContext. To declare a resource provider reference named
OEMSJMSDReference, use:
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 OEMSJMSDReference, and the JNDI name of a resource provider
queue is Queues/MY_QUEUE, then that RP queue is accessible to the application and JMS Connector at JNDI
location java:comp/resource/OEMSJMSDReference/Queues/MY_QUEUE. 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.
Note: You will need to replace "localhost" with the database host name, "5521" with
the database port, and "db" with the database SID.
Then tell OC4J where the data-sources.xml file has been placed by adding a
<data-sources> element to orion-application.xml:
<data-sources path="data-sources.xml"/>
Note: The data-sources path is relative to the orion-application.xml file.
Finally, set the datasource for the resource provider reference by adding a
<property> subelement to the previously created
<resource-provider> element:
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 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 OEMS JMS (Database).
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 http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd,
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:
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.
To declare a JMS Connector instance named OEMSJMSDRAInstanceName (and bound to JNDI location OEMSJMSDRAInstanceName) where
the resource adapter module is OEMSJMSDRADeploy.rar, add the following to the
oc4j-connectors.xml file:
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
or visible to all applications by:
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
This demo uses a local JMS Connector instance.
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:
Many of the same considerations for RP connection
factores also apply to 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
OEMSJMSDRASubcontext. 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:
JNDI Location
Java interface
OEMSJMSDRASubcontext/MyQCF
javax.jms.QueueConnectionFactory
OEMSJMSDRASubcontext/MyTCF
javax.jms.TopicConnectionFactory
OEMSJMSDRASubcontext/MyXAQCF
javax.jms.XAQueueConnectionFactory
OEMSJMSDRASubcontext/MyXATCF
javax.jms.XATopicConnectionFactory
How to Create RA Connection Factories...
To create an RA connection factory at JNDI location
OEMSJMSDRASubcontext/MyCF, insert the following into
oc4j-ra.xml (as a sub-element of the <oc4j-connector-factories>
element):
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 ConnectionFactories/CF, add a
<config-property> element which sets the
"jndiLocation" property:
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.)
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".)
For this demo...
The RA must provide the following destinations:
JNDI Location
Java interface
OEMSJMSDRASubcontext/MyQ
javax.jms.Queue
OEMSJMSDRASubcontext/MyT
javax.jms.Topic
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
OEMSJMSDRASubcontext/MyQ, insert the following into
oc4j-connectors.xml (as a sub-element of the <connector> element created
previously):
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):
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:
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 OEMSJMSDRASubcontext/MyCF:
<resource-ref-mapping
location = "OEMSJMSDRASubcontext/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 OEMSJMSDRASubcontext/MyXACF:
<resource-ref-mapping
location = "OEMSJMSDRASubcontext/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
OEMSJMSDRASubcontext/MyT by adding this to the
orion-application.xml file:
<message-destination-ref-mapping
location = "OEMSJMSDRASubcontext/MyT"
name = "jms/PlayerResponseDestination"/>
and this to the orion-ejb-jar.xml file:
<message-destination-ref-mapping
location = "OEMSJMSDRASubcontext/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 OEMSJMSDRASubcontext/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 = "OEMSJMSDRASubcontext/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
OEMSJMSDRASubcontext/MyQ in the orion-ejb-jar.xml
file:
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:
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:
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
OEMSJMSDRASubcontext/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.
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.)
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
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-oemsjmsd.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".
db.host - the hostname of the database OEMS JMS (Database) should use.
Defaults to DB_HOST env variable.
db.sid - the SID of the database OEMS JMS (Database) should use.
Defaults to DB_SID env variable.
db.port - the port number of the database OEMS JMS (Database) should use.
Defaults to DB_PORT env variable.
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:
If db.host, db.sid, or db.port is changed, update
%HOWTO_HOME%/src/META-INF/data-sources.xml
as well. The url definition in that file should have the following
format:
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-oemsjmsd.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 OEMS JMS (Database).