001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.transaction.jta;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021:
022: import javax.transaction.NotSupportedException;
023: import javax.transaction.SystemException;
024: import javax.transaction.Transaction;
025: import javax.transaction.UserTransaction;
026:
027: import org.springframework.transaction.TransactionDefinition;
028: import org.springframework.transaction.TransactionSystemException;
029: import org.springframework.util.ClassUtils;
030:
031: /**
032: * Special {@link JtaTransactionManager} variant for Oracle OC4J (10.1.3 and higher).
033: * Supports the full power of Spring's transaction definitions on OC4J's
034: * transaction coordinator, <i>beyond standard JTA</i>: transaction names
035: * and per-transaction isolation levels.
036: *
037: * <p>Uses OC4J's special <code>begin(name)</code> method to start a JTA transaction,
038: * in orderto make <b>Spring-driven transactions visible in OC4J's transaction
039: * monitor</b>. In case of Spring's declarative transactions, the exposed name will
040: * (by default) be the fully-qualified class name + "." + method name.
041: *
042: * <p>Supports a <b>per-transaction isolation level</b> through OC4J's corresponding
043: * <code>OC4JTransaction.setTransactionIsolation(int)</code> method. This will
044: * apply the specified isolation level (e.g. ISOLATION_SERIALIZABLE) to all
045: * JDBC Connections that participate in the given transaction.
046: *
047: * <p>Automatically detects the available OC4J server version and adapts accordingly.
048: * Supports the "com.evermind.server" package in OC4J 10.1.3.2 as well as the
049: * "oracle.j2ee.transaction" package in later OC4J versions.
050: *
051: * <p>By default, the JTA UserTransaction and TransactionManager handles are
052: * fetched directly from OC4J's <code>TransactionUtility</code> in 10.1.3.2+.
053: * This can be overridden by specifying "userTransaction"/"userTransactionName"
054: * and "transactionManager"/"transactionManagerName", passing in existing handles
055: * or specifying corresponding JNDI locations to look up.
056: *
057: * <p>Thanks to Oracle for donating the original version of this extended OC4J
058: * integration code to the Spring project!
059: *
060: * @author Paul Parkinson
061: * @author Juergen Hoeller
062: * @since 2.0.3
063: * @see org.springframework.transaction.TransactionDefinition#getName
064: * @see org.springframework.transaction.TransactionDefinition#getIsolationLevel
065: * @see oracle.j2ee.transaction.OC4JTransactionManager#begin(String)
066: * @see oracle.j2ee.transaction.OC4JTransaction#setTransactionIsolation
067: * @see oracle.j2ee.transaction.TransactionUtility
068: */
069: public class OC4JJtaTransactionManager extends JtaTransactionManager {
070:
071: private static final String TRANSACTION_UTILITY_CLASS_NAME = "oracle.j2ee.transaction.TransactionUtility";
072:
073: private static final String TRANSACTION_MANAGER_CLASS_NAME = "oracle.j2ee.transaction.OC4JTransactionManager";
074:
075: private static final String TRANSACTION_CLASS_NAME = "oracle.j2ee.transaction.OC4JTransaction";
076:
077: private static final String FALLBACK_TRANSACTION_MANAGER_CLASS_NAME = "com.evermind.server.ApplicationServerTransactionManager";
078:
079: private static final String FALLBACK_TRANSACTION_CLASS_NAME = "com.evermind.server.ApplicationServerTransaction";
080:
081: private Method beginWithNameMethod;
082:
083: private Method setTransactionIsolationMethod;
084:
085: public void afterPropertiesSet() throws TransactionSystemException {
086: super .afterPropertiesSet();
087: loadOC4JTransactionClasses();
088: }
089:
090: protected UserTransaction retrieveUserTransaction()
091: throws TransactionSystemException {
092: try {
093: Class transactionUtilityClass = getClass().getClassLoader()
094: .loadClass(TRANSACTION_UTILITY_CLASS_NAME);
095: Method getInstanceMethod = transactionUtilityClass
096: .getMethod("getInstance", new Class[0]);
097: Object transactionUtility = getInstanceMethod.invoke(null,
098: new Object[0]);
099: logger
100: .debug("Retrieving JTA UserTransaction from OC4J TransactionUtility");
101: Method getUserTransactionMethod = transactionUtility
102: .getClass().getMethod("getOC4JUserTransaction",
103: new Class[0]);
104: return (UserTransaction) getUserTransactionMethod.invoke(
105: transactionUtility, new Object[0]);
106: } catch (ClassNotFoundException ex) {
107: logger
108: .debug("Could not find OC4J 10.1.3.2 TransactionUtility: "
109: + ex);
110: // Return null to make the superclass perform its standard J2EE lookup,
111: // which will work on earlier OC4J versions.
112: return null;
113: } catch (InvocationTargetException ex) {
114: throw new TransactionSystemException(
115: "OC4J's TransactionUtility.getOC4JUserTransaction() method failed",
116: ex.getTargetException());
117: } catch (Exception ex) {
118: throw new TransactionSystemException(
119: "Could not invoke OC4J's TransactionUtility.getOC4JUserTransaction() method",
120: ex);
121: }
122: }
123:
124: private void loadOC4JTransactionClasses()
125: throws TransactionSystemException {
126: // Find available OC4J API (in "oracle.j2ee.transaction" or "com.evermind.server")
127: Class transactionManagerClass = null;
128: Class transactionClass = null;
129: try {
130: transactionManagerClass = getClass().getClassLoader()
131: .loadClass(TRANSACTION_MANAGER_CLASS_NAME);
132: transactionClass = getClass().getClassLoader().loadClass(
133: TRANSACTION_CLASS_NAME);
134: } catch (ClassNotFoundException ex) {
135: try {
136: transactionManagerClass = getClass()
137: .getClassLoader()
138: .loadClass(
139: FALLBACK_TRANSACTION_MANAGER_CLASS_NAME);
140: transactionClass = getClass().getClassLoader()
141: .loadClass(FALLBACK_TRANSACTION_CLASS_NAME);
142: } catch (ClassNotFoundException ex2) {
143: throw new TransactionSystemException(
144: "Could not initialize OC4JJtaTransactionManager because OC4J API classes are not available",
145: ex);
146: }
147: }
148:
149: // Cache reflective Method references for later use.
150: if (transactionManagerClass.isInstance(getUserTransaction())) {
151: this .beginWithNameMethod = ClassUtils.getMethodIfAvailable(
152: transactionManagerClass, "begin",
153: new Class[] { String.class });
154: this .setTransactionIsolationMethod = ClassUtils
155: .getMethodIfAvailable(transactionClass,
156: "setTransactionIsolation",
157: new Class[] { int.class });
158: logger
159: .info("Support for OC4J transaction names and isolation levels available");
160: } else {
161: logger
162: .info("Support for OC4J transaction names and isolation levels not available");
163: }
164: }
165:
166: protected void doJtaBegin(JtaTransactionObject txObject,
167: TransactionDefinition definition)
168: throws NotSupportedException, SystemException {
169:
170: int timeout = determineTimeout(definition);
171: applyTimeout(txObject, timeout);
172:
173: // Apply transaction name, if any, through the extended OC4J transaction begin method.
174: if (this .beginWithNameMethod != null
175: && definition.getName() != null) {
176: /*
177: oracle.j2ee.transaction.OC4JTransactionManager otm = (oracle.j2ee.transaction.OC4JTransactionManager) ut;
178: otm.begin(definition.getName());
179: */
180: try {
181: this .beginWithNameMethod.invoke(txObject
182: .getUserTransaction(),
183: new Object[] { definition.getName() });
184: } catch (InvocationTargetException ex) {
185: throw new TransactionSystemException(
186: "OC4J's UserTransaction.begin(String) method failed",
187: ex.getTargetException());
188: } catch (Exception ex) {
189: throw new TransactionSystemException(
190: "Could not invoke OC4J's UserTransaction.begin(String) method",
191: ex);
192: }
193: } else {
194: // No OC4J UserTransaction available or no transaction name specified
195: // -> standard JTA begin call.
196: txObject.getUserTransaction().begin();
197: }
198:
199: // Specify isolation level, if any, through the corresponding OC4J transaction method.
200: if (this .setTransactionIsolationMethod != null) {
201: if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
202: try {
203: Transaction tx = getTransactionManager()
204: .getTransaction();
205: /*
206: oracle.j2ee.transaction.OC4JTransaction otx = (oracle.j2ee.transaction.OC4JTransaction) tx;
207: otx.setTransactionIsolation(definition.getIsolationLevel());
208: */
209: Integer isolationLevel = new Integer(definition
210: .getIsolationLevel());
211: this .setTransactionIsolationMethod.invoke(tx,
212: new Object[] { isolationLevel });
213: } catch (InvocationTargetException ex) {
214: throw new TransactionSystemException(
215: "OC4J's Transaction.setTransactionIsolation(int) method failed",
216: ex.getTargetException());
217: } catch (Exception ex) {
218: throw new TransactionSystemException(
219: "Could not invoke OC4J's Transaction.setTransactionIsolation(int) method",
220: ex);
221: }
222: }
223: } else {
224: applyIsolationLevel(txObject, definition
225: .getIsolationLevel());
226: }
227: }
228:
229: public Transaction createTransaction(String name, int timeout)
230: throws NotSupportedException, SystemException {
231: if (this .beginWithNameMethod != null && name != null) {
232: UserTransaction ut = getUserTransaction();
233: if (timeout >= 0) {
234: ut.setTransactionTimeout(timeout);
235: }
236: try {
237: this .beginWithNameMethod.invoke(ut,
238: new Object[] { name });
239: } catch (InvocationTargetException ex) {
240: if (ex.getTargetException() instanceof NotSupportedException) {
241: throw (NotSupportedException) ex
242: .getTargetException();
243: } else if (ex.getTargetException() instanceof SystemException) {
244: throw (SystemException) ex.getTargetException();
245: } else if (ex.getTargetException() instanceof RuntimeException) {
246: throw (RuntimeException) ex.getTargetException();
247: } else {
248: throw new SystemException(
249: "OC4J's begin(String) method failed with an unexpected error: "
250: + ex.getTargetException());
251: }
252: } catch (Exception ex) {
253: throw new SystemException(
254: "Could not invoke OC4J's UserTransaction.begin(String) method: "
255: + ex);
256: }
257: return getTransactionManager().getTransaction();
258: }
259:
260: else {
261: // No name specified - standard JTA is sufficient.
262: return super.createTransaction(name, timeout);
263: }
264: }
265:
266: }
|