001: /*
002: * HA-JDBC: High-Availability JDBC
003: * Copyright (c) 2004-2008 Paul Ferraro
004: *
005: * This library is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU Lesser General Public License as published by the
007: * Free Software Foundation; either version 2.1 of the License, or (at your
008: * option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
013: * for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public License
016: * along with this library; if not, write to the Free Software Foundation,
017: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: ferraro@users.sourceforge.net
020: */
021: package net.sf.hajdbc.sql.xa;
022:
023: import java.lang.reflect.Method;
024: import java.util.Map;
025: import java.util.Set;
026: import java.util.concurrent.ConcurrentHashMap;
027: import java.util.concurrent.ConcurrentMap;
028: import java.util.concurrent.locks.Lock;
029:
030: import javax.sql.XAConnection;
031: import javax.sql.XADataSource;
032: import javax.transaction.xa.XAResource;
033: import javax.transaction.xa.Xid;
034:
035: import net.sf.hajdbc.Database;
036: import net.sf.hajdbc.LockManager;
037: import net.sf.hajdbc.sql.AbstractChildInvocationHandler;
038: import net.sf.hajdbc.sql.DatabaseWriteInvocationStrategy;
039: import net.sf.hajdbc.sql.DriverReadInvocationStrategy;
040: import net.sf.hajdbc.sql.InvocationStrategy;
041: import net.sf.hajdbc.sql.Invoker;
042: import net.sf.hajdbc.sql.SQLProxy;
043: import net.sf.hajdbc.util.reflect.Methods;
044:
045: /**
046: * @author Paul Ferraro
047: *
048: */
049: @SuppressWarnings("nls")
050: public class XAResourceInvocationHandler
051: extends
052: AbstractChildInvocationHandler<XADataSource, XAConnection, XAResource> {
053: private static final Set<Method> driverReadMethodSet = Methods
054: .findMethods(XAResource.class, "getTransactionTimeout",
055: "isSameRM");
056: private static final Set<Method> databaseWriteMethodSet = Methods
057: .findMethods(XAResource.class, "setTransactionTimeout");
058: private static final Set<Method> transactionMethodSet = Methods
059: .findMethods(XAResource.class, "commit", "end", "forget",
060: "prepare", "recover", "rollback", "start");
061: private static final Method startMethod = Methods.getMethod(
062: XAResource.class, "start", Xid.class, Integer.TYPE);
063: private static final Set<Method> commitRollbackMethods = Methods
064: .findMethods(XAResource.class, "commit", "rollback");
065:
066: private ConcurrentMap<Xid, Lock> lockMap = new ConcurrentHashMap<Xid, Lock>();
067:
068: /**
069: * @param connection
070: * @param proxy
071: * @param invoker
072: * @param objectMap
073: * @throws Exception
074: */
075: protected XAResourceInvocationHandler(XAConnection connection,
076: SQLProxy<XADataSource, XAConnection> proxy,
077: Invoker<XADataSource, XAConnection, XAResource> invoker,
078: Map<Database<XADataSource>, XAResource> objectMap)
079: throws Exception {
080: super (connection, proxy, invoker, XAResource.class, objectMap);
081: }
082:
083: /**
084: * @see net.sf.hajdbc.sql.AbstractInvocationHandler#getInvocationStrategy(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
085: */
086: @Override
087: protected InvocationStrategy<XADataSource, XAResource, ?> getInvocationStrategy(
088: XAResource resource, Method method, Object[] parameters)
089: throws Exception {
090: if (driverReadMethodSet.contains(method)) {
091: return new DriverReadInvocationStrategy<XADataSource, XAResource, Object>();
092: }
093:
094: if (databaseWriteMethodSet.contains(method)) {
095: return new DatabaseWriteInvocationStrategy<XADataSource, XAResource, Object>(
096: this .cluster.getNonTransactionalExecutor());
097: }
098:
099: if (transactionMethodSet.contains(method)) {
100: final InvocationStrategy<XADataSource, XAResource, Object> strategy = new DatabaseWriteInvocationStrategy<XADataSource, XAResource, Object>(
101: this .cluster.getTransactionalExecutor());
102:
103: if (method.equals(startMethod)) {
104: Xid xid = (Xid) parameters[0];
105:
106: final Lock lock = this .cluster.getLockManager()
107: .readLock(LockManager.GLOBAL);
108:
109: // Lock may already exist if we're resuming a suspended transaction
110: Lock existingLock = this .lockMap.putIfAbsent(xid, lock);
111:
112: if (existingLock == null) {
113: return new InvocationStrategy<XADataSource, XAResource, Object>() {
114: @Override
115: public Object invoke(
116: SQLProxy<XADataSource, XAResource> proxy,
117: Invoker<XADataSource, XAResource, Object> invoker)
118: throws Exception {
119: lock.lock();
120:
121: return strategy.invoke(proxy, invoker);
122: }
123: };
124: }
125: }
126:
127: if (commitRollbackMethods.contains(method)) {
128: final Lock lock = this .lockMap.remove(parameters[0]);
129:
130: return new InvocationStrategy<XADataSource, XAResource, Object>() {
131: @Override
132: public Object invoke(
133: SQLProxy<XADataSource, XAResource> proxy,
134: Invoker<XADataSource, XAResource, Object> invoker)
135: throws Exception {
136: try {
137: return strategy.invoke(proxy, invoker);
138: } finally {
139: if (lock != null) {
140: lock.unlock();
141: }
142: }
143: }
144: };
145: }
146:
147: return strategy;
148: }
149:
150: return super
151: .getInvocationStrategy(resource, method, parameters);
152: }
153:
154: /**
155: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#close(java.lang.Object, java.lang.Object)
156: */
157: @Override
158: protected void close(XAConnection connection, XAResource resource) {
159: // Do nothing
160: }
161: }
|