001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Emmanuel Cecchet.
019: * Contributor(s): _________________________.
020: */package org.continuent.sequoia.common.locks;
021:
022: import java.util.Iterator;
023: import java.util.LinkedList;
024: import java.util.List;
025:
026: import org.continuent.sequoia.controller.requests.AbstractRequest;
027:
028: /**
029: * This class defines a <code>TransactionLogicalLock</code> which is an
030: * exclusive lock that let the owner of the lock acquire several times the lock
031: * (but it needs to be released only once). Acquire is not blocking but just a
032: * hint whether the lock has been granted or the request has been queued.
033: *
034: * @author <a href="mailto:emmanuel.cecchet@emicnetworks.com">Emmanuel Cecchet</a>
035: * @version 1.0
036: */
037: public class TransactionLogicalLock {
038: private boolean isLocked = false;
039:
040: /** Transaction id of the lock holder. */
041: private long locker;
042:
043: /** <code>ArrayList</code> of <code>WaitingListElement</code>. */
044: private LinkedList waitingList = new LinkedList();
045:
046: /**
047: * The element stored in the waiting list is the waiting request and the
048: * transaction id of the request.
049: */
050: protected class WaitingListElement {
051: /** Waiting request */
052: AbstractRequest request;
053:
054: /** Transaction id of the request waiting. */
055: long transactionId;
056:
057: /**
058: * Creates a new <code>WaitingListElement</code> instance.
059: *
060: * @param request the waiting request.
061: * @param transactionId the transaction id of the request waiting.
062: */
063: WaitingListElement(AbstractRequest request, long transactionId) {
064: this .request = request;
065: this .transactionId = transactionId;
066: }
067:
068: /**
069: * Returns the transaction id of the request waiting.
070: *
071: * @return an <code>int</code> value
072: */
073: public long getTransactionId() {
074: return transactionId;
075: }
076:
077: /**
078: * Returns the waiting request.
079: *
080: * @return a <code>Thread</code> value
081: */
082: public AbstractRequest getRequest() {
083: return request;
084: }
085: }
086:
087: /**
088: * Acquires an exclusive lock on this table. If the lock is already held by
089: * the same transaction as the given request, this method is non-blocking else
090: * the caller is blocked until the transaction holding the lock releases it at
091: * commit/rollback time.
092: *
093: * @param request request asking for the lock (timeout field is used and
094: * updated upon waiting)
095: * @return boolean true is the lock has been successfully acquired, false on
096: * timeout or error
097: * @see #release(long)
098: */
099: public boolean acquire(AbstractRequest request) {
100: long tid = request.getTransactionId();
101:
102: synchronized (this ) {
103: if (!isLocked) { // Lock is free, take it
104: locker = tid;
105: isLocked = true;
106: return true;
107: } else {
108: if (locker == tid)
109: return true; // We already have the lock
110: else { // Wait for the lock
111: if (!isWaiting(tid)) { // We are not yet in the waiting list, append to the queue
112: WaitingListElement wle = new WaitingListElement(
113: request, tid);
114: waitingList.addLast(wle);
115: }
116: return false;
117: }
118: }
119: }
120: }
121:
122: /**
123: * Returns the transaction id of the lock owner. The return value is undefined
124: * if the lock is not owned (usually it is the last owner).
125: *
126: * @return int the transaction id.
127: */
128: public long getLocker() {
129: return locker;
130: }
131:
132: /**
133: * Returns <code>true</code> if the lock is owned by someone.
134: *
135: * @return <code>boolean</code> value
136: */
137: public boolean isLocked() {
138: return isLocked;
139: }
140:
141: /**
142: * Returns <code>true</code> if the given transaction id is contained in
143: * this lock waiting queue.
144: *
145: * @param transactionId a transaction id
146: * @return a <code>boolean</code> value
147: */
148: public synchronized boolean isWaiting(long transactionId) {
149: for (Iterator iter = waitingList.iterator(); iter.hasNext();) {
150: WaitingListElement e = (WaitingListElement) iter.next();
151: if (e.getTransactionId() == transactionId)
152: return true;
153: }
154: return false;
155: }
156:
157: /**
158: * Releases the lock on this table or remove the transaction from the waiting
159: * list if it was not holding the lock.
160: *
161: * @param transactionId the transaction releasing the lock
162: * @return true if the transaction had the lock or was in the waiting list
163: * @see #acquire(AbstractRequest)
164: */
165: public synchronized boolean release(long transactionId) {
166: if (!waitingList.isEmpty()) {
167: if (locker == transactionId) { // We were holding the lock, give it to the first in the waiting list
168: locker = ((WaitingListElement) waitingList
169: .removeFirst()).getTransactionId();
170: return true;
171: }
172:
173: // We were in the waiting list, remove ourselves from the list
174: for (Iterator iter = waitingList.iterator(); iter.hasNext();) {
175: WaitingListElement e = (WaitingListElement) iter.next();
176: if (e.getTransactionId() == transactionId) {
177: iter.remove();
178: return true;
179: }
180: }
181: return false;
182: } else {
183: if (locker == transactionId) {
184: isLocked = false;
185: return true;
186: } else
187: return false;
188: }
189: }
190:
191: /**
192: * Returns the list of requests waiting for this lock.
193: *
194: * @return list of <code>WaitingListElement</code> type objects.
195: */
196: protected List getWaitingList() {
197: return waitingList;
198: }
199: }
|