001: /*
002: * MessageService: The message service daemon
003: * Copyright (C) 2006-2007 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * IDLock.java
020: */
021:
022: // the package path
023: package com.rift.coad.daemon.messageservice;
024:
025: // java imports
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.LinkedHashMap;
029: import java.util.HashMap;
030: import java.util.concurrent.ConcurrentHashMap;
031: import javax.naming.Context;
032: import javax.naming.InitialContext;
033: import javax.transaction.xa.XAException;
034: import javax.transaction.xa.XAResource;
035: import javax.transaction.xa.Xid;
036:
037: // logging import
038: import org.apache.log4j.Logger;
039:
040: // coadunation imports
041: import com.rift.coad.util.transaction.TransactionManager;
042:
043: /**
044: * This object is responsible for creating a lock based on the string id passed
045: * in.
046: *
047: * @author Bret Chaldecott
048: */
049: public class IDLock implements XAResource {
050:
051: /**
052: * This class is responsible for representing a lock based on an id.
053: */
054: public class Lock {
055:
056: // private member variables
057: private String id = null;
058: private Xid owner = null;
059: private int referenceCount = 0;
060:
061: /**
062: * The constructor of the lock object.
063: */
064: public Lock(String id) {
065: this .id = id;
066: }
067:
068: /**
069: * The getter for the id value.
070: *
071: * @return The value of the id for this object.
072: */
073: public String getId() {
074: return id;
075: }
076:
077: /**
078: * This method increments the reference count.
079: *
080: * @return The integer reference count.
081: */
082: public synchronized int incrementReferenceCount() {
083: return ++referenceCount;
084: }
085:
086: /**
087: * This method decrements the reference count to this object and returns
088: * the result of this.
089: *
090: * @return The integer reference count.
091: */
092: public synchronized int decrementReferenceCount() {
093: return --referenceCount;
094: }
095:
096: /**
097: * This method attempts to lock this object based on the owner id
098: * passed in
099: *
100: * @param owner The owner of this lock.
101: * @exception IDLockException
102: */
103: public synchronized void lock(Xid owner) throws IDLockException {
104: while (this .owner != null) {
105: try {
106: wait();
107: } catch (Exception ex) {
108: log.error("Failed to lock this object : "
109: + ex.getMessage(), ex);
110: throw new IDLockException(
111: "Failed to lock this object : "
112: + ex.getMessage(), ex);
113: }
114: }
115: this .owner = owner;
116: }
117:
118: /**
119: * This method unlocks the object.
120: */
121: public synchronized void unlock() {
122: this .owner = null;
123: notify();
124: }
125: }
126:
127: // singletons
128: private static IDLock singleton = null;
129:
130: // log object
131: private Logger log = Logger.getLogger(IDLock.class.getName());
132:
133: // private member variables
134: private Map transactionMap = new ConcurrentHashMap();
135: private Map lockMap = new HashMap();
136: private ThreadLocal currentLock = new ThreadLocal();
137:
138: /**
139: * Creates a new instance of IDLock
140: */
141: private IDLock() {
142: }
143:
144: /**
145: * This method returns an instance of the IDLock object.
146: *
147: * @return A reference to the id lock singleton.
148: */
149: public synchronized static IDLock getInstance() {
150: if (singleton == null) {
151: singleton = new IDLock();
152: }
153: return singleton;
154: }
155:
156: /**
157: * This method creates a lock based on the supplied id.
158: *
159: * @param id The id of the key to lock
160: * @exception IDLockException
161: */
162: public void lock(String id) throws IDLockException {
163: try {
164: Lock lock = null;
165: synchronized (lockMap) {
166: lock = (Lock) lockMap.get(id);
167: if (lock == null) {
168: lock = new Lock(id);
169: lockMap.put(id, lock);
170: }
171: lock.incrementReferenceCount();
172: }
173: currentLock.set(lock);
174: TransactionManager.getInstance().bindResource(this , false);
175: } catch (Exception ex) {
176: log.error("Failed to lock the id [" + id + "] : "
177: + ex.getMessage(), ex);
178: throw new IDLockException("Failed to lock the id [" + id
179: + "] : " + ex.getMessage(), ex);
180: }
181: }
182:
183: /**
184: * This method is responsible for handling the committing of a transaction
185: * identified by the xid.
186: *
187: * @param xid The id of the transaction to commit.
188: * @param onePhase If true a one phase commit should be used.
189: * @exception XAException
190: */
191: public void commit(Xid xid, boolean b) throws XAException {
192: try {
193: Lock lock = (Lock) transactionMap.remove(xid);
194: lock.unlock();
195: synchronized (lockMap) {
196: if (lock.decrementReferenceCount() <= 0) {
197: lockMap.remove(lock.getId());
198: }
199: }
200: } catch (Exception ex) {
201: log.error("Failed to roll back the changes : "
202: + ex.getMessage(), ex);
203: throw new XAException("Failed to roll back the changes : "
204: + ex.getMessage());
205: }
206: }
207:
208: /**
209: * The resource manager has dissociated this object from the transaction.
210: *
211: * @param xid The id of the transaction that is getting ended.
212: * @param flags The flags associated with this operation.
213: * @exception XAException
214: */
215: public void end(Xid xid, int i) throws XAException {
216: }
217:
218: /**
219: * The transaction has been completed and must be forgotten.
220: *
221: * @param xid The id of the transaction to forget.
222: * @exception XAException
223: */
224: public void forget(Xid xid) throws XAException {
225: try {
226: Lock lock = (Lock) transactionMap.remove(xid);
227: lock.unlock();
228: synchronized (lockMap) {
229: if (lock.decrementReferenceCount() <= 0) {
230: lockMap.remove(lock.getId());
231: }
232: }
233: } catch (Exception ex) {
234: log.error("Failed to roll back the changes : "
235: + ex.getMessage(), ex);
236: throw new XAException("Failed to roll back the changes : "
237: + ex.getMessage());
238: }
239: }
240:
241: /**
242: * This method returns the transaction timeout for this object.
243: *
244: * @return The int containing the transaction timeout.
245: * @exception XAException
246: */
247: public int getTransactionTimeout() throws XAException {
248: return -1;
249: }
250:
251: /**
252: * This method returns true if this object is the resource manager getting
253: * queried.
254: *
255: * @return TRUE if this is the resource manager, FALSE if not.
256: * @param xaResource The resource to perform the check against.
257: * @exception XAException
258: */
259: public boolean isSameRM(XAResource xAResource) throws XAException {
260: return this == xAResource;
261: }
262:
263: /**
264: * This is called before a transaction is committed.
265: *
266: * @return The results of the transaction.
267: * @param xid The id of the transaction to check against.
268: * @exception XAException
269: */
270: public int prepare(Xid xid) throws XAException {
271: return XAResource.XA_OK;
272: }
273:
274: /**
275: * This method returns the list of transaction branches for this resource
276: * manager.
277: *
278: * @return The list of resource branches.
279: * @param flags The flags
280: * @exception XAException
281: */
282: public Xid[] recover(int i) throws XAException {
283: return null;
284: }
285:
286: /**
287: * This method is called to roll back the specified transaction.
288: *
289: * @param xid The id of the transaction to roll back.
290: * @exception XAException
291: */
292: public void rollback(Xid xid) throws XAException {
293: try {
294: Lock lock = (Lock) transactionMap.remove(xid);
295: lock.unlock();
296: synchronized (lockMap) {
297: if (lock.decrementReferenceCount() <= 0) {
298: lockMap.remove(lock.getId());
299: }
300: }
301: } catch (Exception ex) {
302: log.error("Failed to roll back the changes : "
303: + ex.getMessage(), ex);
304: throw new XAException("Failed to roll back the changes : "
305: + ex.getMessage());
306: }
307: }
308:
309: /**
310: * This method sets the transaction timeout for this resource manager.
311: *
312: * @return TRUE if the transaction timeout can be set successfully.
313: * @param transactionTimeout The new transaction timeout value.
314: * @exception XAException
315: */
316: public boolean setTransactionTimeout(int i) throws XAException {
317: return true;
318: }
319:
320: /**
321: * This method is called to start a transaction on a resource manager.
322: *
323: * @param xid The id of the new transaction.
324: * @param flags The flags associated with the transaction.
325: * @exception XAException
326: */
327: public void start(Xid xid, int i) throws XAException {
328: try {
329: Lock lock = (Lock) currentLock.get();
330: lock.lock(xid);
331: transactionMap.put(xid, lock);
332: } catch (Exception ex) {
333: log.error("Failed to start the transaction : "
334: + ex.getMessage(), ex);
335: throw new XAException("Failed to start the transaction : "
336: + ex.getMessage());
337: }
338: }
339:
340: }
|