This document summarises configurations for consuming and producing JMS messages using OracleAQ. It is assumed that you have a passing knowledge of Interlok and its configuration. No Oracle knowledge is assumed, though you should have some passing familiarity with SQL. You will be expected to be able to manipulate and compile java classes exported from Oracle. This guide has been built with Oracle 10g in mind. However if you happen to be using later Oracle versions, the only changes should be to the java archive files you will need to copy from your Oracle installation to your Interlok installation, mentioned later in this guide
If you are an Oracle expert and everything is already setup, you can probably ignore almost all of the Oracle specific configuration instructions and do your own thing. If you aren’t an Oracle expert and are setting up an Oracle instance from scratch, then you need to pay careful attention to the Oracle specific configuration instructions.
Getting Started
Interlok Pre-requisites
Before you can start Interlok that is able to consume or produce from or to OracleAQ, you must first install the Interlok OracleAQ optional component into your Interlok installation. All available Interlok sub components are shipped with the standard Interlok installation. Simply navigate to the optional directory at the root of your Interlok installation and from there the oracle
subdirectory. Copy the java archive file named interlok-jms-oracleaq.jar
to your lib directory in the root of your Interlok installation. Finally you will also need to copy any required java archives for client access into the lib directory of your Interlok installation directory. Generally speaking this will be all jdbc14.jar
and aqapi.jar
, however additional jars may be required depending on the connection method. All configuration examples for can be found in the docs/optional/example-xml directory as normal.
Oracle Configuration
The required oracle configuration covers a number of steps:
- Create the tablespace to be used
- Creating the required roles and privileges
- Create the users
- Create the AQ Queue
Using your preferred SQL Client, login to oracle as a DBA user and create a new tablespace.
create tablespace aq
logging
datafile 'C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\AQ'
size 32m
autoextend on
next 32m maxsize 2048m
extent management local;
Next, create the AQ Administrator and AQ User roles.
-- AQ administrator
DROP ROLE my_aq_adm_role;
CREATE ROLE my_aq_adm_role;
--
GRANT CONNECT, RESOURCE, aq_administrator_role TO my_aq_adm_role;
-- AQ users
DROP ROLE my_aq_user_role;
CREATE ROLE my_aq_user_role;
--
GRANT CREATE SESSION, aq_user_role TO my_aq_user_role;
--
EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(PRIVILEGE=>'ENQUEUE_ANY',GRANTEE=>'my_aq_user_role', ADMIN_OPTION=>FALSE);
EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(PRIVILEGE=>'DEQUEUE_ANY',GRANTEE=>'my_aq_user_role', ADMIN_OPTION=>FALSE);
Create users matching the roles that you have just created.
-- Create the AQADM user.
DROP USER aqadm CASCADE;
CREATE USER aqadm IDENTIFIED BY aqadm
DEFAULT TABLESPACE aq
TEMPORARY TABLESPACE temp
QUOTA 0 ON system
QUOTA UNLIMITED ON aq
PROFILE default;
GRANT my_aq_adm_role TO aqadm;
-- Create the AQUSER
DROP USER aquser CASCADE;
CREATE USER aquser IDENTIFIED BY aquser
DEFAULT TABLESPACE aq
TEMPORARY TABLESPACE temp
QUOTA 0 ON system
QUOTA UNLIMITED ON aq
PROFILE default;
GRANT my_aq_user_role TO aquser;
Finally, create the AQ Queue. This step must be done as the AQ Administrator user, so reconnect using the AQ Administrator credentials (if you used the example above, that will be aqadm/aqadm):
EXEC DBMS_AQADM.STOP_QUEUE (queue_name => 'message_queue');
EXEC DBMS_AQADM.DROP_QUEUE (queue_name => 'message_queue');
EXEC DBMS_AQADM.DROP_QUEUE_TABLE (queue_table => 'queue_message_table');
EXEC DBMS_AQADM.CREATE_QUEUE_TABLE (queue_table => 'queue_message_table', queue_payload_type => 'SYS.AQ$_JMS_TEXT_MESSAGE');
EXEC DBMS_AQADM.CREATE_QUEUE (queue_name => 'message_queue', queue_table => 'queue_message_table');
EXEC DBMS_AQADM.START_QUEUE (queue_name => 'message_queue');
Summary
What you have done is create a queue that accepts and delivers standard JMS Text messages. You will now be able to send and receive messages using a oracleaq-implementation as the specific VendorImplementation paying attention to the broker-url that will be used to connect to the Oracle instance.
Interlok Configuration
The associated vendor implementation class that should be used is oracleaq-implementation. Ensure that the broker URL is the full JDBC connection string and that the user associated with the connection has the correct access rights. If you configure an Adapter which 2 channels, one filesystem to Oracle and the other Oracle to filesystem, then you should be able to perform a simple loopback test using Oracle AQ using the queue message_queue
.
Destinations
The produce and consume destinations should always resolve to the AQ Queue that you wish to send and receive from. If you have created the Oracle AQ Queue using one user and intend on connecting using a different user; provided you have sufficient rights as the second user, you can simply specify the owner of the queue using dot notation (e.g. for the above example it would be aqadm.message_queue if you were connecting as aquser
).
Recipient Lists
A feature of using Topics is that you can restrict the delivery of the message to a subset of listeners on the topic. This is achieved within Oracle AQ though the use of recipient lists. As this feature deviates from the standard JMS API; you have to use a different producer - oracle-topic-producer which understands the concept of AQjmsAgent
. The provided implementation allows you to specify a ReceipientList implementation which then provides the AQjmsAgent[] instance which can be used in AQjmsTopicPublisher#publish().
The default implementation, [oracleaq-simple-recipient-list][], allows you to configure multiple nested oracleaq-configured-recipient and oracleaq-metadata-recipient instances. If this type of behaviour is not suitable for your purposes, then you will need to build a custom class that implements the RecipientList interface. However, in most cases, provided you can extract the required information as metadata, the standard implementation should be sufficient.
Additional Message Types
Standard message types are supported by the usual MessageTypeTranslator
implemenstations such as text-message-translator, provided you have setup the AQ Queue with the appropriate queue_payload_type
. Oracle AQ JMS also supports an additional message type that represents internal Oracle objects called AdtMessage
. This is a message whose body contains an Oracle object type, and as such isn’t handled by the standard JMS message types. Using this type of message involves a few more steps before you can start Interlok.
- Create the object type
- Create the queue to use the object type
- Export the object as a java class
- Configure Interlok to map between AdaptrisMessage and the object
This walkthrough will create a simple Oracle object type and configure an appropriate MessageTypeTranslator that is capable of handling the object. Connect to the database as the AQ Administrator (aqadm/aqadm) user to perform the SQL commands.
Oracle Configuration
First of all, create the object type. Here we create an object called queue_message_type_clob
that contains 3 fields; for the purposes of our example, we’ll use 3 different types of field just to demonstrate the mapping capabilities.
CREATE TYPE queue_message_type_clob AS OBJECT (
no NUMBER,
title VARCHAR2(30),
text CLOB
);
/
GRANT EXECUTE ON queue_message_type_clob TO my_aq_user_role;
Next we need to replace the existing queue definition with a new one using queue_message_type_clob
:
EXEC DBMS_AQADM.STOP_QUEUE (queue_name => 'message_queue');
EXEC DBMS_AQADM.DROP_QUEUE (queue_name => 'message_queue');
EXEC DBMS_AQADM.DROP_QUEUE_TABLE (queue_table => 'queue_message_table');
EXEC DBMS_AQADM.CREATE_QUEUE_TABLE (queue_table => 'queue_message_table', queue_payload_type => 'aqadm.queue_message_type_clob');
EXEC DBMS_AQADM.CREATE_QUEUE (queue_name => 'message_queue', queue_table => 'queue_message_table');
EXEC DBMS_AQADM.START_QUEUE (queue_name => 'message_queue');
To make sure that we have done everything correctly, then we can use SQLDeveloper to manually test that the queue was created successfully using the newly created object. You should connect as the AQ user and run the following commands.
--
-- Enqueue message
DECLARE
queue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
message_id RAW(16);
my_message aqadm.queue_message_type_clob;
BEGIN
my_message := aqadm.queue_message_type_clob(
1,
'This is a sample message',
'This message has been posted on ' || TO_CHAR(SYSDATE, 'DD.MM.YYYY HH24:MI:SS'));
DBMS_AQ.ENQUEUE(
queue_name => 'aqadm.message_queue',
enqueue_options => queue_options,
message_properties => message_properties,
payload => my_message,
msgid => message_id);
COMMIT;
END;
/
--
-- Dequeue message
SET SERVEROUTPUT ON;
DECLARE
queue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
message_id RAW(2000);
my_message aqadm.queue_message_type_clob;
BEGIN
DBMS_AQ.DEQUEUE(
queue_name =>'aqadm.message_queue',
dequeue_options => queue_options,
message_properties => message_properties,
payload => my_message,
msgid => message_id
);
COMMIT;
DBMS_OUTPUT.PUT_LINE('Dequeued no: ' || my_message.no);
DBMS_OUTPUT.PUT_LINE('Dequeued title: ' || my_message.title);
DBMS_OUTPUT.PUT_LINE('Dequeued text: ' || my_message.text);
END;
The enqueue
SQL command is an easy way of injecting messages onto the queue (and thus into Interlok) for testing.
Now that the object has been created within Oracle, we need to export so that it is available as a java object. The Oracle installation should provide a tool called JPublisher that allows you to export oracle objects as java source files. The tool is available in the \oc4j\bin directory. With some versions of Oracle, the script may be missing for the Windows platform, but will be available for the UNIX platform. It should be relatively simple to modify the UNIX script to be suitable for Windows. Having created the script (as appropriate) you can run something similar to the following sequence of commands.
echo "SQL QUEUE_MESSAGE_TYPE_CLOB GENERATE QueueMessageTypeClob" >input.txt
jpub -user=aqadm/aqdm -input=input.txt
This will create a number of files in the current work directory. The most important is QueueMessageTypeClob.java
which should be modified to include a package directive, compiled to a .class file, placed in a jar, and made available in the Interlok lib directory.
Interlok Configuration
dynamic-adt-message-translator is provided as a generic message translator that uses reflection to parse the generated wrapper object and map fields directly into the AdaptrisMessage. This should be suitable for most situations, depending on the type of field that is present in the Oracle object type, if not; the other option is to provide your own implementation of AdtMessageTranslator which allows you to control directly how the object will be mapped to and from AdaptrisMessage. Based on the example Oracle object type, we would use a dynamic-adt-message-translator instance with three FieldMapper instances that handle the mapping for No
, Text
and Title
. We will map the Text
field into the payload and all the other fields into metadata.
In addition to standard fields inherited from the parent MessageTypeTranslator
; dynamic-adt-message-translator exposes some additional fields which are detailed below.
Field | Description |
---|---|
adt-classname | The classname of the object previously exported using JPublisher |
move-jms-headers | Whether to move JMS Headers into the object. The default is false as using an AdtMessage may not allow you to specify JMS Headers. |
move-metadata | Whether to move AdaptrisMessage metadata as JMS Properties on the message. The default is false as required metadata must be explicitly mapped into the AdtMessage. |
field-mapper | A List of explicitly configured elements that control how the AdaptrisMessage is mapped into the AdtMessage and vice-versa |
Mapping Fields
There are a number of ways you can map specific AdtMessage fields into AdaptrisMessage and vice versa. All field mapper instances contain some common configuration elements:
Field | Description |
---|---|
field-name | This refers to the specific field within the AdtMessage that you wish to map. From the example, valid values could be No, Text, or Title`. The field name here should match the getter and setter method in the published source file without the get/set prefix |
field-type | This refers to the type of the field |
The supported types of FieldMapper implemenstations are currently oracleaq-metadata-mapper, oracleaq-payload-mapper, oracleaq-message-id-mapper, oracleaq-configured-field and oracleaq-xpath-field.
Field Mapper | Description |
---|---|
oracleaq-metadata-mapper | This maps an item of AdaptrisMessage metadata to a specific AdtMessage field (and vice versa). The metadata-key element determines the metadata key that will be used to derive the content of the mapping. |
oracleaq-payload-mapper | This maps the AdaptrisMessage payload into a specific AdtMessage (and vice versa) |
oracleaq-message-id-mapper | This maps the AdaptrisMessage unique id into a specific AdtMessage field (and vice versa). |
oracleaq-configured-field | This simply sets a specific AdtMessage field to the configured value. AdtMessage fields may not be mapped into AdaptrisMessage using this FieldMapper instance. |
oracleaq-xpath-field | This resolves an XPath against the AdaptrisMessage payload and sets a specific AdtMessage field based on this value. AdtMessage fields may not be mapped into AdaptrisMessage instances using this FieldMapper instance. |
Handling Different Types
The TypeWrapper interface allows you to handle different types of data within the abstract data type. A number of implementations are provided for you; these match the basic JDBC data types.
Type Wrapper | Description |
---|---|
oracleaq-simple-type | This handles the basic primitive types within java; these are configured using the type field. The following types are supported: BIGDECIMAL , BOOLEAN , DOUBLE , FLOAT , INTEGER , and STRING . |
oracleaq-blob-type | This type handles a blob field. As the TypeWrapper interface only supports Strings, various ByteFlavours are supported in order to convert the binary object into a string. The supported byte flavours are BASE64 , PLATFORM , UTF-8 . |
oracleaq-clob-type | This handles a CLOB field. You can set a ClobType.Handler which is either ASCII or CHARACTER, this in turn delegates to either Clob#getAsciiStream() or Clob#getCharacterStream(). If none is specified, ASCII is assumed. |
oracleaq-date-type | This handles a java.sql.Date field. By setting the format string, you can control how the object is converted to and from a String. The conversion will be done using SimpleDateFormat and the specified format string. |
oracleaq-time-type | This type handles a java.sql.Time field. By setting the format string, you can control how the object is converted to and from a String. The conversion will be done using SimpleDateFormat and the specified format string |
oracleaq-timestamp-type | This type handles a java.sql.Timestamp field. By setting the format string, you can control how the object is converted to and from a String. The conversion will be done using SimpleDateFormat and the specified format string |