View Javadoc

1   /*
2    * $Id: AbstractOracleJmsConnector.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  
12  package org.mule.providers.oracle.jms;
13  
14  import org.mule.config.i18n.CoreMessages;
15  import org.mule.providers.ConnectException;
16  import org.mule.providers.jms.JmsConnector;
17  import org.mule.providers.jms.JmsConstants;
18  import org.mule.providers.jms.JmsMessageUtils;
19  import org.mule.transaction.TransactionCoordination;
20  import org.mule.umo.TransactionException;
21  import org.mule.umo.UMOTransaction;
22  import org.mule.umo.lifecycle.InitialisationException;
23  
24  import java.io.Serializable;
25  import java.sql.SQLException;
26  
27  import javax.jms.Connection;
28  import javax.jms.ConnectionFactory;
29  import javax.jms.JMSException;
30  import javax.jms.Session;
31  import javax.naming.NamingException;
32  
33  import oracle.jms.AQjmsSession;
34  import oracle.jms.AdtMessage;
35  import oracle.xdb.XMLType;
36  
37  public abstract class AbstractOracleJmsConnector extends JmsConnector
38  {
39      /**
40       * If a queue's payload is an ADT (Oracle Advanced Data Type), the appropriate
41       * payload factory must be specified in the endpoint's properties. Note: if
42       * <u>all</u> queues are of the same payload type, this property may be set
43       * globally for the connector instead of for each endpoint.
44       */
45      public static final String PAYLOADFACTORY_PROPERTY = "payloadFactory";
46  
47      protected String payloadFactory = null;
48      
49      /**
50       * Some versions of Oracle do not support more than one JMS session per
51       * connection. In this case we need to open a new connection for each session,
52       * otherwise we will get the following error:
53       * {@code JMS-106: Cannot have more than one open Session on a JMSConnection.}
54       */
55      private boolean multipleSessionsPerConnection = false;
56  
57      public AbstractOracleJmsConnector()
58      {
59          super();
60          registerSupportedProtocol("jms");
61      }
62  
63      /**
64       * The Oracle AQ connector supports both the oaq:// and the jms:// protocols.
65       */
66      public String getProtocol()
67      {
68          return "oaq";
69      }
70  
71      /**
72       * The Oracle AQ connector supports both the oaq:// and the jms:// protocols.
73       */
74      public boolean supportsProtocol(String protocol)
75      {
76          // The oaq:// protocol handling breaks the model a bit; you do _not_ need to
77          // qualify the jms protocol with oaq (oaq:jms://) hence we need to override
78          // the
79          // default supportsProtocol() method.
80          return getProtocol().equalsIgnoreCase(protocol) || super.getProtocol().equalsIgnoreCase(protocol);
81      }
82  
83      protected void doConnect() throws ConnectException
84      {
85          try {
86              // Set these to false so that the jndiContext will not be used by the
87              // JmsSupport classes
88              setJndiDestinations(false);
89              setForceJndiDestinations(false);
90  
91              setJmsSupport(new OracleJmsSupport(this, null, false, false));
92          }
93          catch (Exception e) {
94              throw new ConnectException(CoreMessages.failedToCreate("Oracle Jms Connector"), 
95                  e, this);
96          }
97  
98          // Note it doesn't make sense to start a connection at this point
99          // (as the standard JMS Provider does) because a connection will be created
100         // for
101         // each session.
102     }
103 
104     /**
105      * Some versions of Oracle do not support more than one JMS session per
106      * connection. In this case we need to open a new connection for each session,
107      * otherwise we will get the following error:
108      * {@code JMS-106: Cannot have more than one open Session on a JMSConnection.}
109      *
110      * @see #multipleSessionsPerConnection
111      */
112     public Session getSession(boolean transacted, boolean topic) throws JMSException {
113 
114         if (multipleSessionsPerConnection) {
115             return super.getSession(transacted, topic);
116         } else {
117             UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
118 
119             // Check to see if we are already in a session.
120             Session session = getSessionFromTransaction();
121             if (session != null) {
122                 return session;
123             }
124 
125             // Create a new database connection before creating a new session.
126             Connection connection;
127             try {
128                 connection = createConnection();
129             }
130             catch (NamingException e) {
131                 throw new JMSException("Unable to open new database connection: " + e.getMessage());
132             }
133             catch (InitialisationException e) {
134                 throw new JMSException("Unable to open new database connection: " + e.getMessage());
135             }
136 
137             // Create a new session.
138             logger.debug("Retrieving new jms session from connection");
139             session = getJmsSupport().createSession(connection, topic, transacted || tx != null,
140                     getAcknowledgementMode(), isNoLocal());
141             if (tx != null) {
142                 logger.debug("Binding session to current transaction");
143                 try {
144                     tx.bindResource(connection, session);
145                 }
146                 catch (TransactionException e) {
147                     throw new RuntimeException("Could not bind session to current transaction", e);
148                 }
149             }
150             return session;
151         }
152     }
153 
154     /**
155      * Oracle throws a "JMS-102: Feature not supported" error if any of these
156      * "standard" properties are used.
157      */
158     public boolean supportsProperty(String property) {
159         return (!JmsConstants.JMS_REPLY_TO.equalsIgnoreCase(property) && !JmsConstants.JMS_TYPE.equalsIgnoreCase(property));
160     }
161 
162     /**
163      * If the incoming message is an XMLType, return it as a standard
164      * {@code javax.jms.TextMessage}. If the incoming message is any other
165      * AdtMessage, return it as a standard {@code javax.jms.ObjectMessage}.
166      */
167     public javax.jms.Message preProcessMessage(javax.jms.Message message, Session session) throws Exception {
168         Object payload;
169         javax.jms.Message newMessage;
170 
171         if (message instanceof AdtMessage) {
172             payload = ((AdtMessage) message).getAdtPayload();
173 
174             if (payload instanceof XMLType) {
175                 newMessage = session.createTextMessage(((XMLType) payload).getStringVal().trim());
176             } else if (payload instanceof Serializable) {
177                 newMessage = session.createObjectMessage((Serializable) payload);
178             } else {
179                 throw new JMSException("The payload of the incoming AdtMessage must be serializable.");
180             }
181             // TODO Is there a better way to do this?
182             JmsMessageUtils.copyJMSProperties(message, newMessage, this);
183             return newMessage;
184         } else {
185             return message;
186         }
187     }
188 
189     /**
190      * Attempts to close the underlying JDBC connection before closing the JMS
191      * session.
192      *
193      * @see org.mule.providers.jms.JmsConnector.close( javax.jms.Session)
194      */
195     public void close(Session session) throws JMSException {
196         if (session != null) {
197             java.sql.Connection conn = ((AQjmsSession) session).getDBConnection();
198             try {
199                 if (conn != null && !conn.isClosed()) {
200                     conn.commit();
201                     conn.close();
202                 }
203             }
204             catch (SQLException e) {
205                 JMSException ex = new JMSException(e.getMessage());
206                 ex.setLinkedException(e);
207                 throw ex;
208             }
209         }
210     }
211 
212     public abstract java.sql.Connection getJdbcConnection() throws JMSException;
213 
214     public boolean isMultipleSessionsPerConnection() {
215         return multipleSessionsPerConnection;
216     }
217 
218     public void setMultipleSessionsPerConnection(boolean multipleSessionsPerConnection) {
219         this.multipleSessionsPerConnection = multipleSessionsPerConnection;
220     }
221 
222     /**
223      * Oracle has two different factory classes: {@code AQjmsQueueConnectionFactory}
224      * which implements {@code javax.jms.QueueConnectionFactory} and
225      * {@code AQjmsTopicConnectionFactory} which implements
226      * {@code javax.jms.TopicConnectionFactory} so there is no single class to return
227      * in this method.
228      *
229      * @return null
230      */
231     protected ConnectionFactory createConnectionFactory() throws InitialisationException, NamingException {
232         return null;
233     }
234 
235     public String getPayloadFactory()
236     {
237         return payloadFactory;
238     }
239 
240     public void setPayloadFactory(String payloadFactory)
241     {
242         this.payloadFactory = payloadFactory;
243     }
244 }