KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > transaction > memory > OptimisticMapWrapper


1 /*
2  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//transaction/src/java/org/apache/commons/transaction/memory/OptimisticMapWrapper.java,v 1.1 2004/11/18 23:27:18 ozeigermann Exp $
3  * $Revision$
4  * $Date: 2005-02-26 14:16:14 +0100 (Sa, 26 Feb 2005) $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2002 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.commons.transaction.memory;
25
26 import java.io.PrintWriter JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31 import java.util.Collections JavaDoc;
32
33 import org.apache.commons.transaction.locking.ReadWriteLock;
34 import org.apache.commons.transaction.util.LoggerFacade;
35 import org.apache.commons.transaction.util.PrintWriterLogger;
36
37 /**
38  * Wrapper that adds transactional control to all kinds of maps that implement the {@link Map} interface. By using
39  * a naive optimistic transaction control this wrapper has better isolation than {@link TransactionalMapWrapper}, but
40  * may also fail to commit.
41  *
42  * <br>
43  * Start a transaction by calling {@link #startTransaction()}. Then perform the normal actions on the map and
44  * finally either call {@link #commitTransaction()} to make your changes permanent or {@link #rollbackTransaction()} to
45  * undo them.
46  * <br>
47  * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as this will circumvent the transactional mechanism.
48  * Rather clone the value or copy it in a way you see fit and store it back using {@link #put(Object, Object)}.
49  * <br>
50  * <em>Note:</em> This wrapper guarantees isolation level <code>SERIALIZABLE</code>.
51  * <br>
52  * <em>Caution:</em> This implementation might be slow when large amounts of data is changed in a transaction as much references will need to be copied around.
53  *
54  * @version $Revision$
55  * @see TransactionalMapWrapper
56  * @see PessimisticMapWrapper
57  */

58 public class OptimisticMapWrapper extends TransactionalMapWrapper {
59
60     protected static final int COMMIT_TIMEOUT = 1000 * 60; // 1 minute
61
protected static final int ACCESS_TIMEOUT = 1000 * 30; // 30 seconds
62

63     protected Set JavaDoc activeTransactions;
64
65     protected LoggerFacade logger;
66     
67     protected ReadWriteLock commitLock;
68     
69     /**
70      * Creates a new optimistic transactional map wrapper. Temporary maps and sets to store transactional
71      * data will be instances of {@link java.util.HashMap} and {@link java.util.HashSet}.
72      *
73      * @param wrapped map to be wrapped
74      */

75     public OptimisticMapWrapper(Map JavaDoc wrapped) {
76         this(wrapped, new HashMapFactory(), new HashSetFactory());
77     }
78
79     /**
80      * Creates a new optimistic transactional map wrapper. Temporary maps and sets to store transactional
81      * data will be created and disposed using {@link MapFactory} and {@link SetFactory}.
82      *
83      * @param wrapped map to be wrapped
84      * @param mapFactory factory for temporary maps
85      * @param setFactory factory for temporary sets
86      */

87     public OptimisticMapWrapper(Map JavaDoc wrapped, MapFactory mapFactory, SetFactory setFactory) {
88         this(wrapped, mapFactory, setFactory, new PrintWriterLogger(new PrintWriter JavaDoc(System.out),
89                 OptimisticMapWrapper.class.getName(), false));
90     }
91
92     /**
93      * Creates a new optimistic transactional map wrapper. Temporary maps and sets to store transactional
94      * data will be created and disposed using {@link MapFactory} and {@link SetFactory}.
95      *
96      * @param wrapped map to be wrapped
97      * @param mapFactory factory for temporary maps
98      * @param setFactory factory for temporary sets
99      * @param logger
100      * generic logger used for all kinds of logging
101      */

102     public OptimisticMapWrapper(Map JavaDoc wrapped, MapFactory mapFactory, SetFactory setFactory, LoggerFacade logger) {
103         super(wrapped, mapFactory, setFactory);
104         activeTransactions = Collections.synchronizedSet(new HashSet JavaDoc());
105         this.logger = logger;
106         commitLock = new ReadWriteLock("COMMIT", logger);
107     }
108     
109     
110     public void startTransaction() {
111         if (getActiveTx() != null) {
112             throw new IllegalStateException JavaDoc(
113                 "Active thread " + Thread.currentThread() + " already associated with a transaction!");
114         }
115         CopyingTxContext context = new CopyingTxContext();
116         activeTransactions.add(context);
117         setActiveTx(context);
118     }
119
120     public void rollbackTransaction() {
121         TxContext txContext = getActiveTx();
122         super.rollbackTransaction();
123         activeTransactions.remove(txContext);
124     }
125
126     public void commitTransaction() throws ConflictException {
127         commitTransaction(false);
128     }
129
130     public void commitTransaction(boolean force) throws ConflictException {
131         TxContext txContext = getActiveTx();
132         
133         if (txContext == null) {
134             throw new IllegalStateException JavaDoc(
135                 "Active thread " + Thread.currentThread() + " not associated with a transaction!");
136         }
137
138         if (txContext.status == STATUS_MARKED_ROLLBACK) {
139             throw new IllegalStateException JavaDoc("Active thread " + Thread.currentThread() + " is marked for rollback!");
140         }
141         
142         try {
143             // in this final commit phase we need to be the only one access the map
144
// to make sure no one adds an entry after we checked for conflicts
145
commitLock.acquireWrite(txContext, COMMIT_TIMEOUT);
146
147             if (!force) {
148                 Object JavaDoc conflictKey = checkForConflicts();
149                 if (conflictKey != null) {
150                     throw new ConflictException(conflictKey);
151                 }
152             }
153     
154             activeTransactions.remove(txContext);
155             copyChangesToConcurrentTransactions();
156             super.commitTransaction();
157             
158         } catch (InterruptedException JavaDoc e) {
159             // XXX a bit dirty ;)
160
throw new ConflictException(e);
161         } finally {
162             commitLock.release(txContext);
163         }
164     }
165
166     // TODO: Shouldn't we return a collection rather than a single key here?
167
public Object JavaDoc checkForConflicts() {
168         CopyingTxContext txContext = (CopyingTxContext) getActiveTx();
169
170         Set JavaDoc keys = txContext.changedKeys();
171         Set JavaDoc externalKeys = txContext.externalChangedKeys();
172
173         for (Iterator JavaDoc it2 = keys.iterator(); it2.hasNext();) {
174             Object JavaDoc key = it2.next();
175             if (externalKeys.contains(key)) {
176                 return key;
177             }
178         }
179         return null;
180     }
181
182     protected void copyChangesToConcurrentTransactions() {
183         CopyingTxContext thisTxContext = (CopyingTxContext) getActiveTx();
184
185         for (Iterator JavaDoc it = activeTransactions.iterator(); it.hasNext();) {
186             CopyingTxContext otherTxContext = (CopyingTxContext) it.next();
187
188             // no need to copy data if the other transaction does not access global map anyway
189
if (otherTxContext.cleared)
190                 continue;
191
192             if (thisTxContext.cleared) {
193                 // we will clear everything, so we have to copy everything before
194
otherTxContext.externalChanges.putAll(wrapped);
195             } else // no need to check if we have already copied everthing
196
{
197
198                 for (Iterator JavaDoc it2 = thisTxContext.changes.entrySet().iterator(); it2.hasNext();) {
199                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it2.next();
200                     Object JavaDoc value = wrapped.get(entry.getKey());
201                     if (value != null) {
202                         // undo change
203
otherTxContext.externalChanges.put(entry.getKey(), value);
204                     } else {
205                         // undo add
206
otherTxContext.externalDeletes.add(entry.getKey());
207                     }
208                 }
209
210                 for (Iterator JavaDoc it2 = thisTxContext.deletes.iterator(); it2.hasNext();) {
211                     // undo delete
212
Object JavaDoc key = it2.next();
213                     Object JavaDoc value = wrapped.get(key);
214                     otherTxContext.externalChanges.put(key, value);
215                 }
216             }
217         }
218
219     }
220
221     public class CopyingTxContext extends TxContext {
222         protected Map JavaDoc externalChanges;
223         protected Map JavaDoc externalAdds;
224         protected Set JavaDoc externalDeletes;
225
226         protected CopyingTxContext() {
227             super();
228             externalChanges = mapFactory.createMap();
229             externalDeletes = setFactory.createSet();
230             externalAdds = mapFactory.createMap();
231         }
232
233         protected Set JavaDoc externalChangedKeys() {
234             Set JavaDoc keySet = new HashSet JavaDoc();
235             keySet.addAll(externalDeletes);
236             keySet.addAll(externalChanges.keySet());
237             keySet.addAll(externalAdds.keySet());
238             return keySet;
239         }
240
241         protected Set JavaDoc changedKeys() {
242             Set JavaDoc keySet = new HashSet JavaDoc();
243             keySet.addAll(deletes);
244             keySet.addAll(changes.keySet());
245             keySet.addAll(adds.keySet());
246             return keySet;
247         }
248
249         protected Set JavaDoc keys() {
250             try {
251                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
252                 Set JavaDoc keySet = super.keys();
253                 keySet.removeAll(externalDeletes);
254                 keySet.addAll(externalAdds.keySet());
255                 return keySet;
256             } catch (InterruptedException JavaDoc e) {
257                 return null;
258             } finally {
259                 commitLock.release(this);
260             }
261         }
262
263         protected Object JavaDoc get(Object JavaDoc key) {
264             try {
265                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
266
267                 if (deletes.contains(key)) {
268                     // reflects that entry has been deleted in this tx
269
return null;
270                 }
271     
272                 Object JavaDoc changed = changes.get(key);
273                 if (changed != null) {
274                     return changed;
275                 }
276     
277                 Object JavaDoc added = adds.get(key);
278                 if (added != null) {
279                     return added;
280                 }
281     
282                 if (cleared) {
283                     return null;
284                 } else {
285                     if (externalDeletes.contains(key)) {
286                         // reflects that entry has been deleted in this tx
287
return null;
288                     }
289     
290                     changed = externalChanges.get(key);
291                     if (changed != null) {
292                         return changed;
293                     }
294     
295                     added = externalAdds.get(key);
296                     if (added != null) {
297                         return added;
298                     }
299     
300                     // not modified in this tx
301
return wrapped.get(key);
302                 }
303             } catch (InterruptedException JavaDoc e) {
304                 return null;
305             } finally {
306                 commitLock.release(this);
307             }
308         }
309
310         protected void put(Object JavaDoc key, Object JavaDoc value) {
311             try {
312                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
313                 super.put(key, value);
314             } catch (InterruptedException JavaDoc e) {
315             } finally {
316                 commitLock.release(this);
317             }
318         }
319         
320         protected void remove(Object JavaDoc key) {
321             try {
322                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
323                 super.remove(key);
324             } catch (InterruptedException JavaDoc e) {
325             } finally {
326                 commitLock.release(this);
327             }
328         }
329
330         protected int size() {
331             try {
332                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
333                 int size = super.size();
334     
335                 size -= externalDeletes.size();
336                 size += externalAdds.size();
337     
338                 return size;
339             } catch (InterruptedException JavaDoc e) {
340                 return -1;
341             } finally {
342                 commitLock.release(this);
343             }
344         }
345
346         protected void clear() {
347             try {
348                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
349                 super.clear();
350                 externalDeletes.clear();
351                 externalChanges.clear();
352                 externalAdds.clear();
353             } catch (InterruptedException JavaDoc e) {
354             } finally {
355                 commitLock.release(this);
356             }
357         }
358
359         protected void merge() {
360             try {
361                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
362                 super.merge();
363             } catch (InterruptedException JavaDoc e) {
364             } finally {
365                 commitLock.release(this);
366             }
367         }
368         
369         protected void dispose() {
370             try {
371                 commitLock.acquireRead(this, ACCESS_TIMEOUT);
372                 super.dispose();
373                 setFactory.disposeSet(externalDeletes);
374                 externalDeletes = null;
375                 mapFactory.disposeMap(externalChanges);
376                 externalChanges = null;
377                 mapFactory.disposeMap(externalAdds);
378                 externalAdds = null;
379             } catch (InterruptedException JavaDoc e) {
380             } finally {
381                 commitLock.release(this);
382             }
383         }
384
385         protected void finalize() throws Throwable JavaDoc {
386             activeTransactions.remove(this);
387             super.finalize();
388         }
389     }
390 }
391
Popular Tags