blob: fbe3f60f848982ca132dcd4b1ed0e52cb4858e8f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.ee;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.AsmAdaptor;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.NoTransactionException;
import serp.bytecode.BCClass;
import serp.bytecode.Project;
/**
* {@link ManagedRuntime} implementation that allows synchronization with a
* WebSphere managed transaction.
*
* <P>
* WebSphere Application Server does not expose the TransactionManager to an
* application. Instead it provides a proprietary interface to register for
* synchronization and obtain transaction ids.
*
* <P>
* WASManagedRuntime provides the wrapper classes needed to interact with the
* WAS proprietary interface and the OpenJPA kernel.
*
* @author Kevin Sutter
*/
public class WASManagedRuntime extends AbstractManagedRuntime
implements ManagedRuntime, Configurable {
private static final Localizer _loc =
Localizer.forPackage(WASManagedRuntime.class);
private Object _extendedTransaction = null;
private Method _getGlobalId = null;
private Method _registerSync = null;
private OpenJPAConfiguration _conf = null;
private Log _log = null;
/**
* Gets an extendedJTATransaction from JNDI and creates a transaction
* wrapper
*/
@Override
public javax.transaction.TransactionManager getTransactionManager()
throws Exception {
return new WASTransaction();
}
/**
* Transaction wrapper for WebSphere. WebSphere exposes a subset of the
* Transaction and TransactionManager interfaces to the customer. Any
* methods which are not exposed by WebSphere will throw an
* IllegalStateException to the caller.
*
* <P>
* Methods supported by WAS are
* <UL>
* <LI>RegisterSynchronization </LI>
* <LI>GetStatus</LI>
* </UL>
*/
class WASTransaction implements javax.transaction.TransactionManager,
javax.transaction.Transaction {
@Override
public int getStatus() throws SystemException {
int rval;
try {
if (getGlobalId() != null) {
rval = Status.STATUS_ACTIVE;
} else {
rval = Status.STATUS_NO_TRANSACTION;
}
} catch (Exception e) {
throw new NoTransactionException(_loc
.get("was-transaction-id-exception")).setCause(e);
}
return rval;
}
/**
* Provides a Transaction wrapper. The transaction wrapper mayb only be
* used to determine the status of the current transaction. WebSphere
* does not allow direct control of container transactions.
*
* @return A WebSphere transaction wrapper.
*/
@Override
public Transaction getTransaction() throws SystemException {
return this;
}
/**
* Register for synchronization with a WebSphere managed transaction via
* the extendedJTATransaction interface.
*/
@Override
public void registerSynchronization(Synchronization arg0)
throws IllegalStateException, RollbackException, SystemException {
if (_extendedTransaction != null) {
try {
_registerSync.invoke(_extendedTransaction,
new Object[] { new WASSynchronization(arg0) });
} catch (Exception e) {
throw new InvalidStateException(_loc
.get("was-reflection-exception")).setCause(e);
}
} else {
throw new InvalidStateException(_loc.get("was-lookup-error"));
}
}
/**
* Gets the GlobalTransaction ID of the WebSphere managed transaction.
* If no Global Transaction is active null will be returned.
*
* @return Null if a global transaction is not active or if an error
* occurs. byte[] id if a global transaction is active.
*/
private byte[] getGlobalId() {
try {
return (byte[]) _getGlobalId.invoke(_extendedTransaction, null);
} catch (Exception e) {
throw new InvalidStateException(_loc
.get("was-reflection-exception")).setCause(e);
}
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public void begin() throws NotSupportedException, SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"begin"));
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public void commit() throws HeuristicMixedException,
HeuristicRollbackException, IllegalStateException,
RollbackException, SecurityException, SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"commit"));
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public void resume(Transaction arg0) throws IllegalStateException,
InvalidTransactionException, SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"resume"));
}
/**
* Unimplemented, WAS does not provide this level of control. Log a
* trace instead of throwing an exception. Rollback may be used in
* some error paths, throwing another exception may result in the
* original exception being lost.
*/
@Override
public void rollback() throws IllegalStateException, SecurityException,
SystemException {
if (_log.isTraceEnabled()) {
_log.trace(_loc.get("was-unsupported-op", "rollback"));
}
}
/**
* Unimplemented, WAS does not provide this level of control. Log a
* trace instead of throwing an exception. SetRollbackOnly may be used
* in some error paths, throwing another exception may result in the
* original exception being lost.
*/
@Override
public void setRollbackOnly() throws IllegalStateException,
SystemException {
if (_log.isTraceEnabled()) {
_log.trace(_loc.get("was-unsupported-op", "setRollbackOnly"));
}
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public void setTransactionTimeout(int arg0) throws SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"setTransactionTimeout"));
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public Transaction suspend() throws SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"suspend"));
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public boolean delistResource(XAResource arg0, int arg1)
throws IllegalStateException, SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"delistResource"));
}
/**
* Unimplemented, WAS does not provide this level of control. Throws an
* IllegalStateException
*/
@Override
public boolean enlistResource(XAResource arg0)
throws IllegalStateException, RollbackException, SystemException {
throw new InvalidStateException(_loc.get("was-unsupported-op",
"enlistResource"));
}
}
/**
* WASSynchronization wrapper. This class translates the WAS proprietary
* synchronization callback methods to javax.transaction.Synchronization
* methods.
*
* <P>
* This class implements the
* com.ibm.websphere.jtaextensions.SynchronizationCallback interface. Since
* SynchronizationCallback is not available at compile time we use Serp to
* add the interface to the class after all classes have been compiled.
*
* <P>
* SynchronizationCallback is expected to be available whenever this class
* is instantiated, therefore this class should only be used when running in
* WebSphere.
*
* @see org.apache.openjpa.util.WASTransformer
*/
static class WASSynchronization {
Synchronization _sync = null;
WASSynchronization(Synchronization sync) {
_sync = sync;
}
/**
* AfterCompletion wrapper. Translates the WAS proprietary call to a
* javax.transaction.Synchronization call.
*/
public void afterCompletion(int localTransactionId,
byte[] globalTransactionId, boolean committed) {
if (_sync != null) {
if (committed) {
_sync.afterCompletion(Status.STATUS_COMMITTED);
} else {
_sync.afterCompletion(Status.STATUS_UNKNOWN);
}
}
}
/**
* BeforeCompletion wrapper. Translates WAS proprietary call to a
* javax.transaction.Synchronization call.
*/
public void beforeCompletion(int arg0, byte[] arg1) {
if (_sync != null) {
_sync.beforeCompletion();
}
}
}
/**
* Caches a copy of the configuration. The configuration is used to obtain
* the logger and classloader.
*/
@Override
public void setConfiguration(Configuration conf) {
_conf = (OpenJPAConfiguration) conf;
_log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
}
/**
* EndConfiguration stub.
*/
@Override
public void endConfiguration() {
try {
Context ctx = new InitialContext();
try {
_extendedTransaction =
ctx.lookup("java:comp/websphere/ExtendedJTATransaction");
} finally {
ctx.close();
}
Class extendedJTATransaction = Class.forName(
"com.ibm.websphere.jtaextensions.ExtendedJTATransaction");
Class synchronizationCallback = Class.forName(
"com.ibm.websphere.jtaextensions.SynchronizationCallback");
_registerSync = extendedJTATransaction.getMethod(
"registerSynchronizationCallbackForCurrentTran",
new Class[] { synchronizationCallback });
_getGlobalId = extendedJTATransaction.
getMethod("getGlobalId", null);
} catch (Exception e) {
throw new InvalidStateException(_loc
.get("was-reflection-exception"), e).setFatal(true);
}
}
/**
* StartConfiguration stub.
*/
@Override
public void startConfiguration() {
// Nothing to do
}
/**
* Class that will be modified
*/
static final String CLASS =
"org.apache.openjpa.ee.WASManagedRuntime$WASSynchronization";
/**
* Interface which will be added
*/
static final String INTERFACE =
"com.ibm.websphere.jtaextensions.SynchronizationCallback";
public static void main(String[] args)
throws IOException {
Project project = new Project();
InputStream in = WASManagedRuntime.class.getClassLoader()
.getResourceAsStream(CLASS.replace('.', '/') + ".class");
BCClass bcClass = project.loadClass(in);
String [] interfaces = bcClass.getInterfaceNames();
if(interfaces != null) {
for (String anInterface : interfaces) {
if (anInterface.equals(INTERFACE)) {
return;
}
}
}
bcClass.declareInterface(INTERFACE);
AsmAdaptor.write(bcClass);
}
@Override
public void setRollbackOnly(Throwable cause)
throws Exception {
// there is no generic support for setting the rollback cause
getTransactionManager().getTransaction().setRollbackOnly();
}
@Override
public Throwable getRollbackCause()
throws Exception {
// there is no generic support for setting the rollback cause
return null;
}
}