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.cms.repository;
019:
020: import java.util.ArrayList;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.List;
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.avalon.framework.service.ServiceException;
030: import org.apache.avalon.framework.service.ServiceManager;
031: import org.apache.lenya.ac.Identity;
032: import org.apache.lenya.cms.observation.ObservationRegistry;
033: import org.apache.lenya.cms.observation.RepositoryEvent;
034: import org.apache.lenya.cms.observation.RepositoryListener;
035: import org.apache.lenya.transaction.ConcurrentModificationException;
036: import org.apache.lenya.transaction.IdentityMap;
037: import org.apache.lenya.transaction.IdentityMapImpl;
038: import org.apache.lenya.transaction.Lock;
039: import org.apache.lenya.transaction.Lockable;
040: import org.apache.lenya.transaction.TransactionException;
041: import org.apache.lenya.transaction.TransactionLock;
042: import org.apache.lenya.transaction.Transactionable;
043: import org.apache.lenya.transaction.UnitOfWork;
044: import org.apache.lenya.transaction.UnitOfWorkImpl;
045: import org.apache.lenya.util.Assert;
046:
047: /**
048: * Repository session.
049: */
050: public class SessionImpl extends AbstractLogEnabled implements Session {
051:
052: protected static final String UNMODIFIABLE_SESSION_ID = "unmodifiable";
053: private ServiceManager manager;
054: private Identity identity;
055:
056: /**
057: * Ctor.
058: * @param identity The identity.
059: * @param modifiable Determines if the repository items in this session can be modified.
060: * @param manager The service manager.
061: * @param logger The logger.
062: */
063: public SessionImpl(Identity identity, boolean modifiable,
064: ServiceManager manager, Logger logger) {
065:
066: ContainerUtil.enableLogging(this , logger);
067:
068: Assert.notNull("service manager", manager);
069: this .manager = manager;
070:
071: this .identityMap = new IdentityMapImpl(logger);
072: this .identity = identity;
073: this .id = modifiable ? createUuid() : UNMODIFIABLE_SESSION_ID;
074:
075: ObservationRegistry registry = null;
076: try {
077: registry = (ObservationRegistry) this .manager
078: .lookup(ObservationRegistry.ROLE);
079: addListener(registry);
080: } catch (Exception e) {
081: throw new RuntimeException(e);
082: } finally {
083: if (registry != null) {
084: this .manager.release(registry);
085: }
086: }
087:
088: if (modifiable) {
089: this .unitOfWork = new UnitOfWorkImpl(this .identityMap,
090: this .identity, getLogger());
091: }
092: }
093:
094: protected String createUuid() {
095: String id;
096: UUIDGenerator generator = null;
097: try {
098:
099: generator = (UUIDGenerator) this .manager
100: .lookup(UUIDGenerator.ROLE);
101: id = generator.nextUUID();
102: } catch (Exception e) {
103: throw new RuntimeException(e);
104: } finally {
105: if (generator == null) {
106: this .manager.release(generator);
107: }
108: }
109: return id;
110: }
111:
112: public Identity getIdentity() {
113: return this .identity;
114: }
115:
116: private UnitOfWork unitOfWork;
117: private SharedItemStore sharedItemStore;
118:
119: /**
120: * @return The unit of work.
121: */
122: protected UnitOfWork getUnitOfWork() {
123: if (this .unitOfWork == null) {
124: throw new RuntimeException("This session [" + getId()
125: + "] is not modifiable!");
126: }
127: return this .unitOfWork;
128: }
129:
130: private boolean committing = false;
131:
132: /**
133: * Commits the transaction.
134: * @throws RepositoryException if an error occurs.
135: * @throws ConcurrentModificationException if a transactionable has been modified by another
136: * session.
137: */
138: public synchronized void commit() throws RepositoryException,
139: ConcurrentModificationException {
140:
141: savePersistables();
142:
143: this .committing = true;
144:
145: try {
146: synchronized (TransactionLock.LOCK) {
147:
148: getUnitOfWork().commit();
149: getSharedItemStore().clear();
150: }
151: } catch (ConcurrentModificationException e) {
152: throw e;
153: } catch (TransactionException e) {
154: throw new RepositoryException(e);
155: }
156:
157: for (Iterator i = this .events.iterator(); i.hasNext();) {
158: RepositoryEvent event = (RepositoryEvent) i.next();
159: for (Iterator l = this .listeners.iterator(); l.hasNext();) {
160: RepositoryListener listener = (RepositoryListener) l
161: .next();
162: listener.eventFired(event);
163: }
164: }
165: this .events.clear();
166: this .committing = false;
167: }
168:
169: /**
170: * Save all persistable objects to their nodes.
171: * @throws RepositoryException if an error occurs.
172: */
173: protected void savePersistables() throws RepositoryException {
174: Object[] objects = getIdentityMap().getObjects();
175: for (int i = 0; i < objects.length; i++) {
176: if (objects[i] instanceof Node) {
177: Node node = (Node) objects[i];
178: Persistable persistable = node.getPersistable();
179: if (persistable != null && persistable.isModified()) {
180: persistable.save();
181: }
182: }
183: }
184: }
185:
186: /**
187: * Rolls the transaction back.
188: * @throws RepositoryException if an error occurs.
189: */
190: public void rollback() throws RepositoryException {
191: try {
192: synchronized (TransactionLock.LOCK) {
193: getUnitOfWork().rollback();
194: }
195: } catch (TransactionException e) {
196: throw new RepositoryException(e);
197: }
198: this .events.clear();
199: }
200:
201: protected SharedItemStore getSharedItemStore() {
202: if (this .sharedItemStore == null) {
203: try {
204: this .sharedItemStore = (SharedItemStore) this .manager
205: .lookup(SharedItemStore.ROLE);
206: } catch (ServiceException e) {
207: throw new RuntimeException(e);
208: }
209: }
210: return this .sharedItemStore;
211: }
212:
213: /**
214: * @see org.apache.lenya.cms.repository.Session#getRepositoryItem(org.apache.lenya.cms.repository.RepositoryItemFactory,
215: * java.lang.String)
216: */
217: public RepositoryItem getRepositoryItem(
218: RepositoryItemFactory factory, String key)
219: throws RepositoryException {
220: RepositoryItemFactoryWrapper wrapper = new RepositoryItemFactoryWrapper(
221: factory, this );
222: return (RepositoryItem) getIdentityMap().get(wrapper, key);
223: }
224:
225: public void registerNew(Transactionable object)
226: throws TransactionException {
227: getUnitOfWork().registerNew(object);
228: }
229:
230: public void registerDirty(Transactionable object)
231: throws TransactionException {
232: getUnitOfWork().registerDirty(object);
233: }
234:
235: public void registerRemoved(Transactionable object)
236: throws TransactionException {
237: getUnitOfWork().registerRemoved(object);
238: }
239:
240: /**
241: * @param identity The identity.
242: */
243: public void setIdentity(Identity identity) {
244: this .identity = identity;
245: }
246:
247: public boolean isDirty(Transactionable transactionable) {
248: return getUnitOfWork().isDirty(transactionable);
249: }
250:
251: public Lock createLock(Lockable lockable, int version)
252: throws TransactionException {
253: return getUnitOfWork().createLock(lockable, version);
254: }
255:
256: public void removeLock(Lockable lockable)
257: throws TransactionException {
258: getUnitOfWork().removeLock(lockable);
259: }
260:
261: private Set listeners = new HashSet();
262:
263: public void addListener(RepositoryListener listener)
264: throws RepositoryException {
265: if (this .listeners.contains(listener)) {
266: throw new RepositoryException("The listener [" + listener
267: + "] is already registered for node [" + this
268: + "]!");
269: }
270: this .listeners.add(listener);
271: }
272:
273: public boolean isListenerRegistered(RepositoryListener listener) {
274: return this .listeners.contains(listener);
275: }
276:
277: private List events = new ArrayList();
278: private IdentityMap identityMap;
279:
280: public synchronized void enqueueEvent(RepositoryEvent event) {
281: if (!isModifiable()) {
282: throw new RuntimeException(
283: "Can't enqueue event in unmodifiable session!");
284: }
285: if (committing) {
286: throw new RuntimeException(
287: "No events can be queued while the session is being committed. Event: ["
288: + event.getDescriptor() + "]");
289: }
290: Assert.isTrue("event belongs to session",
291: event.getSession() == this );
292: this .events.add(event);
293: }
294:
295: protected IdentityMap getIdentityMap() {
296: return this .identityMap;
297: }
298:
299: public boolean isModifiable() {
300: return this .unitOfWork != null;
301: }
302:
303: private String id;
304:
305: public String getId() {
306: return this.id;
307: }
308:
309: }
|