package org.csc.phynixx.spring.jta; /* * #%L * phynixx-spring * %% * Copyright (C) 2014 - 2015 Christoph Schmidt-Casdorff * %% * Licensed 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. * #L% */ import org.csc.phynixx.common.exceptions.DelegatedRuntimeException; import org.springframework.jdbc.datasource.ConnectionProxy; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import javax.sql.DataSource; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; /** * Created by christoph on 14.12.2014. */ public class TransactionAwareXAResource<C> { ReflectionUtils reflectionUtils; Object targetConnectionFactory =null; Class<C> connectionType; /** * Create a new TransactionAwareDataSourceProxy. */ public TransactionAwareXAResource() { } /** * Create a new TransactionAwareDataSourceProxy. * @param connectionFactory wrapped physical connection */ public TransactionAwareXAResource(Object connectionFactory) { this.targetConnectionFactory=connectionFactory; } public Object getTargetConnectionFactory() { return targetConnectionFactory; } public void setTargetConnectionFactory(Object targetConnectionFactory, Class<C> connectionType) { this.targetConnectionFactory = targetConnectionFactory; this.connectionType=connectionType; } public Class<?> getConnectionType() { return connectionType; } public void setConnectionType(Class<C> connectionType) { this.connectionType = connectionType; } /** * Delegates to DataSourceUtils for automatically participating in Spring-managed * transactions. Throws the original SQLException, if any. * <p>The returned Connection handle implements the ConnectionProxy interface, * allowing to retrieve the underlying target Connection. * @return a transactional Connection if any, a new one else * @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection * @see org.springframework.jdbc.datasource.ConnectionProxy#getTargetConnection */ // @Override // public C getConnection() { // Object ds = getTargetConnectionFactory(); // Assert.state(ds != null, "'targetDataSource' is required"); // return getTransactionAwareConnectionProxy(ds); // } // // /** // * Wraps the given Connection with a proxy that delegates every method call to it // * but delegates {@code close()} calls to DataSourceUtils. // * @param targetDataSource DataSource that the Connection came from // * @return the wrapped Connection // * @see java.sql.Connection#close() // * @see org.springframework.jdbc.datasource.DataSourceUtils#doReleaseConnection // */ // protected C getTransactionAwareConnectionProxy(Object targetDataSource, Class<C> connectionType) { // return (Connection) Proxy.newProxyInstance( // ConnectionProxy.class.getClassLoader(), // new Class[]{ConnectionProxy.class, IObject.class, IConnectionDelegate.class}, // new TransactionAwareInvocationHandler(targetDataSource,connectionType)); // } // // /** // * Determine whether to obtain a fixed target Connection for the proxy // * or to reobtain the target Connection for each operation. // * <p>The default implementation returns {@code true} for all // * standard cases. This can be overridden through the // * {@link #setReobtainTransactionalConnections "reobtainTransactionalConnections"} // * flag, which enforces a non-fixed target Connection within an active transaction. // * Note that non-transactional access will always use a fixed Connection. // * @param targetDataSource the target DataSource // */ // protected boolean shouldObtainFixedConnection(DataSource targetDataSource) { // return (!TransactionSynchronizationManager.isSynchronizationActive() || // !this.reobtainTransactionalConnections); // } // // // /** // * Invocation handler that delegates close calls on JDBC Connections // * to DataSourceUtils for being aware of thread-bound transactions. // */ // private class TransactionAwareInvocationHandler<CC> implements InvocationHandler { // // // Object targetConnectionFactory =null; // // Class<CC> connectionType; // // // private CC target; // // private boolean closed = false; // // public TransactionAwareInvocationHandler(DataSource targetDataSource, Class<CC> connectionType) { // this.targetConnectionFactory = targetConnectionFactory; // this.connectionType=connectionType; // } // // private boolean ismplementedBy(Method method, Class<?> iface) { // try { // return iface.getDeclaredMethod(method.getName(), method.getParameterTypes()) != null; // } catch (NoSuchMethodException e) { // return false; // } // } // // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // // Invocation on ConnectionProxy interface coming in... // // if (method.getName().equals("equals")) { // // Only considered as equal when proxies are identical. // return (proxy == args[0]); // } // else if (method.getName().equals("hashCode")) { // // Use hashCode of Connection proxy. // return System.identityHashCode(proxy); // } // else if (method.getName().equals("toString")) { // // Allow for differentiating between the proxy and the raw Connection. // StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection "); // if (this.target != null) { // sb.append("[").append(this.target.toString()).append("]"); // } // else { // sb.append(" from DataSource [").append(this.targetDataSource).append("]"); // } // return sb.toString(); // } // else if (method.getName().equals("close")) { // // Handle close method: only close if not within a transaction. // DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource); // this.closed = true; // return null; // } // else if (method.getName().equals("isClosed")) { // return this.closed; // } // // if (this.target == null) { // if (this.closed) { // throw new DelegatedRuntimeException()n("Connection handle already closed"); // } // this.target = DataSourceUtils.doGetConnection(this.targetDataSource); // } // Connection actualTarget = this.target; // if (actualTarget == null) { // actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource); // } // // if (method.getName().equals("getTargetConnection")) { // // Handle getTargetConnection method: return underlying Connection. // return actualTarget; // } // // // Invoke method on target Connection. // try { // Object retVal = method.invoke(actualTarget, args); // // // If return value is a Statement, apply transaction timeout. // // Applies to createStatement, prepareStatement, prepareCall. // if (retVal instanceof Statement) { // DataSourceUtils.applyTransactionTimeout((Statement) retVal, this.targetDataSource); // } // // return retVal; // } // catch (InvocationTargetException ex) { // throw ex.getTargetException(); // } // finally { // if (actualTarget != this.target) { // DataSourceUtils.doReleaseConnection(actualTarget, this.targetDataSource); // } // } // } // } }