Oracle JMS AQ

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Oracle JMS AQ

Renato Eschini
Hi all,
is possibile to integrate Oracle JMS AQ with BTM and Tomcat?

I made a lot of experimental configuration with resource loader, context.xml ecc... without success...

I'm bit confusing....

Has someone did it? An example or guideline?

Thanks all!
Reply | Threaded
Open this post in threaded view
|

Re: Oracle JMS AQ

Ludovic Orban
Administrator
Oracle AQ really is a special JMS implementation, to say the least.

You first need to get some queues created with the DBMS_AQJMS PL/SQL package. You then need some privileges:

 * ENQUEUE rights on all queues you're going to send to
 * DEQUEUE rights on all queuse you're going to receive from
 * EXECUTE rights on DBMS_AQJMS
 * EXECUTE rights on DBMS_AQIN

After that's done you're still facing a dilemma: Oracle provides a weird way to get a XAConnectionFactory, via a AQjmsFactory helper class' static method (see: http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b12023/oracle/jms/AQjmsFactory.html) and BTM unfortunately has no support for such an exotic mechanism. So you basically have two choices:

 - Write your own XAConnectionFactory implementation, make it use AQjmsFactory and configure it in BTM. It shouldn't be very hard as there are only two methods to implement (http://www.j2ee.me/j2ee/1.4/docs/api/javax/jms/XAConnectionFactory.html) plus a few getters/setters for configurable data you'll need to call getXAQueueConnectionFactory(). Have a look at the source code of JndiXAConnectionFactory to get an example of how to do this: http://fisheye.codehaus.org/browse/btm/branches/1.3.3/src/bitronix/tm/resource/jms/JndiXAConnectionFactory.java?r=HEAD

 - Wrap the non-XA AQjmsConnectionFactory (http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b12023/oracle/jms/AQjmsConnectionFactory.html)in a LrcXAConnectionFactory (see: http://docs.codehaus.org/display/BTM/LastResourceCommitJms13)

Let me know if that helps.
Reply | Threaded
Open this post in threaded view
|

Re: Oracle JMS AQ

Renato Eschini
Thanks for your replay Ludovic!

I decided to follow the first choice so I implemented XAConnectionFactory, configure it in resource loader propery file, bind it in context.xml (tomcat integration) and write some line of code in my utility class to get Pooling connection from jndi and create session and publisher..... at end of this post I'll publish my solution.

But I have encuntered a problem... I'think XAConnection that I retrive from pool isn't an enlisted resources...  the BTM warning log is explicity when I try to ut.commit() a user transaction:

29-Oct-2009 23:45:19 bitronix.tm.twopc.Preparer prepare
WARNING: executing transaction with 0 enlisted resource

the only logical difference between JndiXAConnectionFactory and my OracleXATopicConnectionFactory is that mine solution implements XATopicConnectionFactory, beacuse I need a topic in my app.


In my numerous tests I have try to insert the correponding XAResource in transaction manager in this way:

BitronixTransactionManager btm = (BitronixTransactionManager) ut;
btm.getTransaction().enlistResource(xaTopicSession.getXAResource());

and btm throw
bitronix.tm.internal.BitronixSystemException: unknown XAResource oracle.jms.AQjmsXAResource@112bc7b, it does not belong to a registered resource

I don't know why this is not registered in btm... can you tell me something about it?

Follow what I did....

1) IMPLEMENTATION OF XATopicConnectionFactory
public class OracleXATopicConnectionFactory implements XATopicConnectionFactory {

        private String driver;
        private String url;
        private String user;
        private String pwd;

        private OracleXADataSource oracleXADataSource = null;
       
        protected void init() throws SQLException {
                if (oracleXADataSource == null) {
                        oracleXADataSource = new OracleXADataSource();
                        oracleXADataSource.setURL(url);
                        oracleXADataSource.setUser(user);
                        oracleXADataSource.setPassword(pwd);
                }
        }

        @Override
        public XAConnection createXAConnection() throws JMSException {
                try {
                        init();
                        XATopicConnectionFactory xaConnectionFactory = AQjmsFactory.getXATopicConnectionFactory(oracleXADataSource);
                        XATopicConnection xaConnection = xaConnectionFactory.createXATopicConnection();
                        return xaConnection;
                } catch (SQLException e) {
                        e.printStackTrace();
                        throw new JMSException(e.getMessage());
                }
        }

        @Override
        public XAConnection createXAConnection(String userName, String password) throws JMSException {
                // I don't use userName and password beacuse I have oracleXADataSource....
                return createXAConnection();
        }
       
        @Override
        public XATopicConnection createXATopicConnection() throws JMSException {
                return (XATopicConnection) createXAConnection();
        }

        @Override
        public XATopicConnection createXATopicConnection(String userName, String password) throws JMSException {
                return (XATopicConnection) createXAConnection(userName, password);
        }

        @Override
        public TopicConnection createTopicConnection() throws JMSException {
                return (XATopicConnection) createXAConnection();
        }

        @Override
        public TopicConnection createTopicConnection(String userName, String password) throws JMSException {
                return (XATopicConnection) createXAConnection(userName, password);
        }

        @Override
        public Connection createConnection() throws JMSException {
                return (XATopicConnection) createXAConnection();
        }

        @Override
        public Connection createConnection(String userName, String password) throws JMSException {
                return (XATopicConnection) createXAConnection(userName, password);
        }

        public String getDriver() {
                return driver;
        }

        public void setDriver(String driver) {
                this.driver = driver;
        }

        public String getUrl() {
                return url;
        }

        public void setUrl(String url) {
                this.url = url;
        }

        public String getUser() {
                return user;
        }

        public void setUser(String user) {
                this.user = user;
        }

        public String getPwd() {
                return pwd;
        }

        public void setPwd(String pwd) {
                this.pwd = pwd;
        }
}




2) RESOURCE.PROPERTIES
# used in iBatis is OK
resource.ds.className=oracle.jdbc.xa.client.OracleXADataSource
resource.ds.uniqueName=jdbc/oracle
resource.ds.maxPoolSize=5
resource.ds.driverProperties.user=JDBC_USER
resource.ds.driverProperties.password=JDBC_PWD
resource.ds.driverProperties.URL=jdbc:oracle:thin:@localhost:1521:XE

# this is for Oracle JMS impl.
resource.t.className=it.inera.oracle.jms.OracleXATopicConnectionFactory
resource.t.uniqueName=jms/oracle
resource.t.maxPoolSize=5
resource.t.driverProperties.driver=oracle.jdbc.xa.client.OracleXADataSource
resource.t.driverProperties.user=AQ_USER
resource.t.driverProperties.pwd=AQ_PWD
resource.t.driverProperties.url=jdbc:oracle:thin:@localhost:1521:XE

3) CONTEXT.XML
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="MyApp" path="/MyApp" reloadable="true">
                <Resource
                        name="jdbc/oracle"
                        auth="Container"
                        type="javax.sql.DataSource"
            factory="bitronix.tm.resource.ResourceObjectFactory"
            uniqueName="jdbc/oracle"
                         />
                <Resource
                        name="jms/oracle"
                        auth="Container"
                        type="javax.sql.DataSource"
            factory="bitronix.tm.resource.ResourceObjectFactory"
            uniqueName="jms/oracle"
                         />
</Context>

4) WEB.XML ENTRY (isn't necessary in tomcat 5.5.X
<resource-env-ref>
          <resource-env-ref-name>jdbc/oracle</resource-env-ref-name>
          <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
        </resource-env-ref>
        <resource-env-ref>
          <resource-env-ref-name>jms/oracle</resource-env-ref-name>
          <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
        </resource-env-ref>       

5) CODE TO OBTAIN XATOPIC IN SERVLET-TOMCAT ENVIRONMENT

Context initContext = new InitialContext();
PoolingConnectionFactory myConnectionFactory = (PoolingConnectionFactory) initContext.lookup("java:comp/env/jms/oracle");
       
UserTransaction ut = (UserTransaction) initContext.lookup("java:comp/UserTransaction");
ut.begin();


Connection connection = myConnectionFactory.createConnection();
JmsConnectionHandle btmJCH = (JmsConnectionHandle) connection;

XAConnection xaConnection = btmJCH.getXAConnection();
xaConnection.start(); // start connection
AQjmsXAConnection aQjmsXAConnection = (AQjmsXAConnection) xaConnection;
XATopicSession xaTopicSession = aQjmsXAConnection.createXATopicSession();


AQjmsXASession qjmsXASession = (AQjmsXASession) xaTopicSession;
Topic t = qjmsXASession.createTopic(Configuration.getJmsOracleXmlMessageTopic());


/*
 * CREATE PAYLOAD IN ORACLE FASHION
 */
StringWriter stringWriter = ....; // contain my XML Payload....
XMLType xml = new XMLType(qjmsXASession.getDBConnection(), stringWriter.toString());
AdtMessage message = ((AQjmsXASession)xaTopicSession).createAdtMessage();
message.setAdtPayload(xml);

TopicPublisher  messageProducer  = (TopicPublisher) xaTopicSession.createProducer(t);
messageProducer.publish(t,message);
 
connection.close();
xaTopicSession.close();


ut.commit();



Against ut.commit(); BTM log a

WARNING: executing transaction with 0 enlisted resource


I think that BTM don't register the XAOracleDataSource or somthing about it....


thank you in advance for your patience :)
Reply | Threaded
Open this post in threaded view
|

Re: Oracle JMS AQ

Renato Eschini
SOLVED!
Now I have jdbc/iBatis + jms/oracle + tomcat in BTM-JTA fashion!!! :)

The problem was in code where I  have create xaconnection... if I call it directly  (point 5 of my previus post) I obtain an unregister XAResource...

####################   WRONG WAY  ##########################
XAConnection xaConnection = btmJCH.getXAConnection();
xaConnection.start(); // start connection
AQjmsXAConnection aQjmsXAConnection = (AQjmsXAConnection) xaConnection;
XATopicSession xaTopicSession = aQjmsXAConnection.createXATopicSession();
####################   WRONG WAY  ##########################

I'll post my solution (the modified point 5 of my previous post), if you want, I can write it in a how-to/wiki article style for publish this experience in BTM wiki.... tell me...

point 1, 2, 3, 4 was correct and wasn't modified... the point 5 was correct in this way

5)  ####################   RIGHT WAY  ##########################
Context initContext = new InitialContext();
PoolingConnectionFactory myConnectionFactory = (PoolingConnectionFactory) initContext.lookup("java:comp/env/jms/oracle");
       
UserTransaction ut = (UserTransaction) initContext.lookup("java:comp/UserTransaction");
ut.begin();

Connection connection = myConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
               
Topic topic = session.createTopic(Configuration.getJmsOracleXmlMessageTopic());

DualSessionWrapper dualSessionWrapper = (DualSessionWrapper)session;
AQjmsXASession aQjmsXASession = (AQjmsXASession) dualSessionWrapper.getSession();
               
StringWriter stringWriter = ....; // contain my XML Payload....
XMLType xml = new XMLType(qjmsXASession.getDBConnection(), stringWriter.toString());
AdtMessage message = ((AQjmsXASession)xaTopicSession).createAdtMessage();
message.setAdtPayload(xml);


MessageProducer publisher = session.createProducer(topic);
publisher.send(message);


ut.commit();

// ut.rollback(); //if somethig was wrong........



Thanks Ludovic!!!!! Thanks BTM!
Reply | Threaded
Open this post in threaded view
|

Re: Oracle JMS AQ

Renato Eschini
Sorry, I have post wrong code in AdtMessage creation line, this is corrected code

5)  ####################   RIGHT WAY  ##########################
Connection connection = myConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
               
Topic topic = session.createTopic(Configuration.getJmsOracleXmlMessageTopic());
               
DualSessionWrapper dualSessionWrapper = (DualSessionWrapper)session;
AQjmsXASession aQjmsXASession = (AQjmsXASession) dualSessionWrapper.getSession();
               
StringWriter stringWriter = new StringWriter();
XMLType xml = new XMLType(aQjmsXASession.getDBConnection(), stringWriter.toString());
AdtMessage message = aQjmsXASession.createAdtMessage();
message.setAdtPayload(xml);
               
               
MessageProducer publisher = session.createProducer(topic);
publisher.send(message);

ut.commit();

// ut.rollback(); //if somethig was wrong........
Reply | Threaded
Open this post in threaded view
|

Re: Oracle JMS AQ

Ludovic Orban
Administrator
I'm happy to hear you finally managed to get Oracle AQ working.

If you write a short tutorial on things one need to do I'll happily include that in the wiki.

Thanks,
Ludovic