001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.lenya.transaction;
019:
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025:
026: import org.apache.avalon.framework.container.ContainerUtil;
027: import org.apache.avalon.framework.logger.AbstractLogEnabled;
028: import org.apache.avalon.framework.logger.Logger;
029: import org.apache.lenya.ac.Identity;
030: import org.apache.lenya.util.Assert;
031:
032: /**
033: * Default implementation of a unit of work.
034: *
035: * @version $Id: UnitOfWorkImpl.java 574827 2007-09-12 08:00:57Z andreas $
036: */
037: public class UnitOfWorkImpl extends AbstractLogEnabled implements
038: UnitOfWork {
039:
040: /**
041: * Ctor.
042: * @param map The identity map to use.
043: * @param identity The identity.
044: * @param logger The logger.
045: */
046: public UnitOfWorkImpl(IdentityMap map, Identity identity,
047: Logger logger) {
048: ContainerUtil.enableLogging(this , logger);
049:
050: Assert.notNull(map);
051: this .identityMap = map;
052: this .identityMap.setUnitOfWork(this );
053:
054: this .identity = identity;
055: }
056:
057: private IdentityMap identityMap;
058:
059: /**
060: * @return The identity map.
061: */
062: public IdentityMap getIdentityMap() {
063: return this .identityMap;
064: }
065:
066: private Set newObjects = new HashSet();
067: private Set modifiedObjects = new HashSet();
068: private Set removedObjects = new HashSet();
069:
070: /**
071: * @see org.apache.lenya.transaction.UnitOfWork#registerNew(org.apache.lenya.transaction.Transactionable)
072: */
073: public void registerNew(Transactionable object)
074: throws TransactionException {
075: this .newObjects.add(object);
076: }
077:
078: /**
079: * @throws TransactionException
080: * @throws LockException
081: * @see org.apache.lenya.transaction.UnitOfWork#registerDirty(org.apache.lenya.transaction.Transactionable)
082: */
083: public void registerDirty(Transactionable object)
084: throws TransactionException {
085: this .modifiedObjects.add(object);
086: }
087:
088: /**
089: * @see org.apache.lenya.transaction.UnitOfWork#registerRemoved(org.apache.lenya.transaction.Transactionable)
090: */
091: public void registerRemoved(Transactionable object)
092: throws TransactionException {
093: this .removedObjects.add(object);
094: }
095:
096: /**
097: * Commit the transaction. We lock this method for the whole class to avoid synchronization
098: * problems.
099: * @see org.apache.lenya.transaction.UnitOfWork#commit()
100: */
101: public void commit() throws TransactionException {
102: if (getLogger().isDebugEnabled()) {
103: getLogger().debug("UnitOfWorkImpl::commit() called");
104: }
105:
106: Set lockedObjects = this .locks.keySet();
107:
108: for (Iterator i = lockedObjects.iterator(); i.hasNext();) {
109: Transactionable t = (Transactionable) i.next();
110: if (t.hasChanged()) {
111: throw new ConcurrentModificationException(t);
112: }
113: }
114:
115: Set involvedObjects = new HashSet();
116: involvedObjects.addAll(this .newObjects);
117: involvedObjects.addAll(this .modifiedObjects);
118: involvedObjects.addAll(this .removedObjects);
119:
120: try {
121: for (Iterator i = involvedObjects.iterator(); i.hasNext();) {
122: Transactionable t = (Transactionable) i.next();
123: t.checkout();
124: }
125:
126: for (Iterator i = this .newObjects.iterator(); i.hasNext();) {
127: Transactionable t = (Transactionable) i.next();
128: t.createTransactionable();
129: t.saveTransactionable();
130: }
131: for (Iterator i = this .modifiedObjects.iterator(); i
132: .hasNext();) {
133: Transactionable t = (Transactionable) i.next();
134: if (getLogger().isDebugEnabled()) {
135: getLogger().debug(
136: "UnitOfWorkImpl::commit() calling save on ["
137: + t + "]");
138: }
139: t.saveTransactionable();
140: }
141: for (Iterator i = this .removedObjects.iterator(); i
142: .hasNext();) {
143: Transactionable t = (Transactionable) i.next();
144: t.deleteTransactionable();
145: }
146:
147: } finally {
148: if (getIdentityMap() != null) {
149: Object[] objects = getIdentityMap().getObjects();
150: for (int i = 0; i < objects.length; i++) {
151: if (objects[i] instanceof Transactionable) {
152: Transactionable t = (Transactionable) objects[i];
153: if (t.isCheckedOutBySession()
154: && !this .removedObjects.contains(t)) {
155: t.checkin();
156: }
157: if (t.isLocked()) {
158: t.unlock();
159: }
160: }
161: }
162: }
163: }
164:
165: resetTransaction();
166:
167: }
168:
169: protected void resetTransaction() {
170: this .modifiedObjects.clear();
171: this .newObjects.clear();
172: this .removedObjects.clear();
173: }
174:
175: private Identity identity;
176:
177: protected Identity getIdentity() {
178: return this .identity;
179: }
180:
181: /**
182: * @see org.apache.lenya.transaction.UnitOfWork#isDirty(org.apache.lenya.transaction.Transactionable)
183: */
184: public boolean isDirty(Transactionable transactionable) {
185: return this .modifiedObjects.contains(transactionable)
186: || this .newObjects.contains(transactionable)
187: || this .removedObjects.contains(transactionable);
188: }
189:
190: /**
191: * Rollback the transaction. We lock this method for the whole class to avoid synchronization
192: * problems.
193: * @see org.apache.lenya.transaction.UnitOfWork#rollback()
194: */
195: public synchronized void rollback() throws TransactionException {
196: if (getLogger().isDebugEnabled()) {
197: getLogger().debug("UnitOfWorkImpl::rollback() called");
198: }
199: if (getIdentityMap() != null) {
200: Object[] objects = getIdentityMap().getObjects();
201: for (int i = 0; i < objects.length; i++) {
202: if (objects[i] instanceof Transactionable) {
203: Transactionable t = (Transactionable) objects[i];
204: if (t.isCheckedOutBySession()) {
205: t.checkin();
206: }
207: if (t.isLocked()) {
208: t.unlock();
209: }
210: }
211: }
212: resetTransaction();
213: }
214: }
215:
216: private Map locks = new HashMap();
217:
218: public Lock createLock(Lockable lockable, int version)
219: throws TransactionException {
220: if (this .locks.containsKey(lockable)) {
221: throw new LockException("A lock is already placed on ["
222: + lockable
223: + "]. A new lock could lead to inconsistent data.");
224: }
225: Lock lock = new Lock(version);
226: this .locks.put(lockable, lock);
227: return lock;
228: }
229:
230: public void removeLock(Lockable lockable)
231: throws TransactionException {
232: if (!this .locks.containsKey(lockable)) {
233: throw new LockException("No lock is already placed on ["
234: + lockable + "]!");
235: }
236: this.locks.remove(lockable);
237: }
238:
239: }
|