Index: SharedItemStateManager.java =================================================================== --- SharedItemStateManager.java (revision 209589) +++ SharedItemStateManager.java (working copy) @@ -38,6 +38,9 @@ import java.util.ArrayList; import java.util.Iterator; +import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock; +import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock; + /** * Shared ItemStateManager. Caches objects returned from a * PersistenceManager. Objects returned by this item state @@ -67,6 +70,11 @@ private VirtualItemStateProvider[] virtualProviders = new VirtualItemStateProvider[0]; /** + * Read-/Write-Lock to synchronize access on this item state manager. + */ + private final ReadWriteLock rwLock = new ReentrantWriterPreferenceReadWriteLock(); + + /** * Creates a new SharedItemStateManager instance. * * @param persistMgr @@ -226,21 +234,28 @@ */ public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException { - // check the virtual root ids (needed for overlay) - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].isVirtualRoot(id)) { - return virtualProviders[i].getItemState(id); + + acquireReadLock(); + + try { + // check the virtual root ids (needed for overlay) + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].isVirtualRoot(id)) { + return virtualProviders[i].getItemState(id); + } } - } - // check internal first - if (hasNonVirtualItemState(id)) { - return getNonVirtualItemState(id); - } - // check if there is a virtual state for the specified item - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].hasItemState(id)) { - return virtualProviders[i].getItemState(id); + // check internal first + if (hasNonVirtualItemState(id)) { + return getNonVirtualItemState(id); } + // check if there is a virtual state for the specified item + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].hasItemState(id)) { + return virtualProviders[i].getItemState(id); + } + } + } finally { + rwLock.readLock().release(); } throw new NoSuchItemStateException(id.toString()); } @@ -271,24 +286,36 @@ * {@inheritDoc} */ public boolean hasItemState(ItemId id) { - if (isCached(id)) { - return true; + + try { + acquireReadLock(); + } catch (ItemStateException e) { + return false; } - // check the virtual root ids (needed for overlay) - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].isVirtualRoot(id)) { + + try { + if (isCached(id)) { return true; } - } - // check if this manager has the item state - if (hasNonVirtualItemState(id)) { - return true; - } - // otherwise check virtual ones - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].hasItemState(id)) { + + // check the virtual root ids (needed for overlay) + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].isVirtualRoot(id)) { + return true; + } + } + // check if this manager has the item state + if (hasNonVirtualItemState(id)) { return true; } + // otherwise check virtual ones + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].hasItemState(id)) { + return true; + } + } + } finally { + rwLock.readLock().release(); } return false; } @@ -319,19 +346,25 @@ public NodeReferences getNodeReferences(NodeReferencesId id) throws NoSuchItemStateException, ItemStateException { - // check persistence manager + acquireReadLock(); + try { - return persistMgr.load(id); - } catch (NoSuchItemStateException e) { - // ignore - } - // check virtual providers - for (int i = 0; i < virtualProviders.length; i++) { + // check persistence manager try { - return virtualProviders[i].getNodeReferences(id); + return persistMgr.load(id); } catch (NoSuchItemStateException e) { // ignore } + // check virtual providers + for (int i = 0; i < virtualProviders.length; i++) { + try { + return virtualProviders[i].getNodeReferences(id); + } catch (NoSuchItemStateException e) { + // ignore + } + } + } finally { + rwLock.readLock().release(); } // throw @@ -343,19 +376,29 @@ */ public boolean hasNodeReferences(NodeReferencesId id) { - // check persistence manager try { - if (persistMgr.exists(id)) { - return true; - } + acquireReadLock(); } catch (ItemStateException e) { - // ignore + return false; } - // check virtual providers - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].hasNodeReferences(id)) { - return true; + + try { + // check persistence manager + try { + if (persistMgr.exists(id)) { + return true; + } + } catch (ItemStateException e) { + // ignore } + // check virtual providers + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].hasNodeReferences(id)) { + return true; + } + } + } finally { + rwLock.readLock().release(); } return false; } @@ -482,139 +525,169 @@ // todo: remember by provider ArrayList virtualRefs = new ArrayList(); - /** - * Validate modified references. Target node of references may - * have been deleted in the meantime. - */ - Iterator iter = local.modifiedRefs(); - while (iter.hasNext()) { - NodeReferences refs = (NodeReferences) iter.next(); - NodeId id = new NodeId(refs.getUUID()); - // if targetid is in virtual provider, transfer to its modified set - for (int i = 0; i < virtualProviders.length; i++) { - VirtualItemStateProvider provider = virtualProviders[i]; - if (provider.hasItemState(id)) { - virtualRefs.add(refs); - refs = null; - break; - } - } - if (refs != null) { - if (refs.hasReferences()) { - if (!local.has(id) && !hasItemState(id)) { - String msg = "Target node " + id - + " of REFERENCE property does not exist"; - throw new ItemStateException(msg); - } - } - shared.modified(refs); - } - } + acquireWriteLock(); - EventStateCollection events = null; - boolean succeeded = false; - try { /** - * Reconnect all items contained in the change log to their - * respective shared item and add the shared items to a - * new change log. + * Validate modified references. Target node of references may + * have been deleted in the meantime. */ - iter = local.modifiedStates(); + Iterator iter = local.modifiedRefs(); while (iter.hasNext()) { - ItemState state = (ItemState) iter.next(); - state.connect(getItemState(state.getId())); - shared.modified(state.getOverlayedState()); + NodeReferences refs = (NodeReferences) iter.next(); + NodeId id = new NodeId(refs.getUUID()); + // if targetid is in virtual provider, transfer to its modified set + for (int i = 0; i < virtualProviders.length; i++) { + VirtualItemStateProvider provider = virtualProviders[i]; + if (provider.hasItemState(id)) { + virtualRefs.add(refs); + refs = null; + break; + } + } + if (refs != null) { + if (refs.hasReferences()) { + if (!local.has(id) && !hasItemState(id)) { + String msg = "Target node " + id + + " of REFERENCE property does not exist"; + throw new ItemStateException(msg); + } + } + shared.modified(refs); + } } - iter = local.deletedStates(); - while (iter.hasNext()) { - ItemState state = (ItemState) iter.next(); - state.connect(getItemState(state.getId())); - shared.deleted(state.getOverlayedState()); - } - iter = local.addedStates(); - while (iter.hasNext()) { - ItemState state = (ItemState) iter.next(); - state.connect(createInstance(state)); - shared.added(state.getOverlayedState()); - } - /* prepare the events */ - if (obsMgr != null) { - events = obsMgr.createEventStateCollection(); - events.createEventStates(root.getUUID(), local, this); - events.prepare(); - } + EventStateCollection events = null; + boolean succeeded = false; - /* Push all changes from the local items to the shared items */ - local.push(); - - /* Store items in the underlying persistence manager */ - long t0 = System.currentTimeMillis(); - persistMgr.store(shared); - succeeded = true; - long t1 = System.currentTimeMillis(); - if (log.isInfoEnabled()) { - log.info("persisting change log " + shared + " took " + (t1 - t0) + "ms"); - } - - } finally { - - /** - * If some store operation was unsuccessful, we have to reload - * the state of modified and deleted items from persistent - * storage. - */ - if (!succeeded) { - local.disconnect(); - - iter = shared.modifiedStates(); + try { + /** + * Reconnect all items contained in the change log to their + * respective shared item and add the shared items to a + * new change log. + */ + iter = local.modifiedStates(); while (iter.hasNext()) { ItemState state = (ItemState) iter.next(); - try { - state.copy(loadItemState(state.getId())); - } catch (ItemStateException e) { - state.discard(); - } + state.connect(getItemState(state.getId())); + shared.modified(state.getOverlayedState()); } - iter = shared.deletedStates(); + iter = local.deletedStates(); while (iter.hasNext()) { ItemState state = (ItemState) iter.next(); - try { - state.copy(loadItemState(state.getId())); - } catch (ItemStateException e) { - state.discard(); - } + state.connect(getItemState(state.getId())); + shared.deleted(state.getOverlayedState()); } - iter = shared.addedStates(); + iter = local.addedStates(); while (iter.hasNext()) { ItemState state = (ItemState) iter.next(); - state.discard(); + state.connect(createInstance(state)); + shared.added(state.getOverlayedState()); } + + /* prepare the events */ + if (obsMgr != null) { + events = obsMgr.createEventStateCollection(); + events.createEventStates(root.getUUID(), local, this); + events.prepare(); + } + + /* Push all changes from the local items to the shared items */ + local.push(); + + /* Store items in the underlying persistence manager */ + long t0 = System.currentTimeMillis(); + persistMgr.store(shared); + succeeded = true; + long t1 = System.currentTimeMillis(); + if (log.isInfoEnabled()) { + log.info("persisting change log " + shared + " took " + (t1 - t0) + "ms"); + } + + } finally { + + /** + * If some store operation was unsuccessful, we have to reload + * the state of modified and deleted items from persistent + * storage. + */ + if (!succeeded) { + local.disconnect(); + + iter = shared.modifiedStates(); + while (iter.hasNext()) { + ItemState state = (ItemState) iter.next(); + try { + state.copy(loadItemState(state.getId())); + } catch (ItemStateException e) { + state.discard(); + } + } + iter = shared.deletedStates(); + while (iter.hasNext()) { + ItemState state = (ItemState) iter.next(); + try { + state.copy(loadItemState(state.getId())); + } catch (ItemStateException e) { + state.discard(); + } + } + iter = shared.addedStates(); + while (iter.hasNext()) { + ItemState state = (ItemState) iter.next(); + state.discard(); + } + } } - } - /* Let the shared item listeners know about the change */ - shared.persisted(); + /* Let the shared item listeners know about the change */ + shared.persisted(); - /* notify virtual providers about node references */ - iter = virtualRefs.iterator(); - while (iter.hasNext()) { - NodeReferences refs = (NodeReferences) iter.next(); - // if targetid is in virtual provider, transfer to its modified set - for (int i = 0; i < virtualProviders.length; i++) { - if (virtualProviders[i].setNodeReferences(refs)) { - break; + /* notify virtual providers about node references */ + iter = virtualRefs.iterator(); + while (iter.hasNext()) { + NodeReferences refs = (NodeReferences) iter.next(); + // if targetid is in virtual provider, transfer to its modified set + for (int i = 0; i < virtualProviders.length; i++) { + if (virtualProviders[i].setNodeReferences(refs)) { + break; + } } } + + /* dispatch the events */ + if (events != null) { + events.dispatch(); + } + } finally { + rwLock.writeLock().release(); } + } - /* dispatch the events */ - if (events != null) { - events.dispatch(); + /** + * Acquires the read lock on this item state manager. + * @throws ItemStateException if the read lock cannot be acquired. + */ + private void acquireReadLock() throws ItemStateException { + try { + rwLock.readLock().acquire(); + } catch (InterruptedException e) { + throw new ItemStateException("Interrupted while acquiring read lock"); } } + /** + * Acquires the write lock on this item state manager. + * @throws ItemStateException if the write lock cannot be acquired. + */ + private void acquireWriteLock() throws ItemStateException { + try { + rwLock.writeLock().acquire(); + } catch (InterruptedException e) { + throw new ItemStateException("Interrupted while acquiring write lock"); + } + } + //----------------------------------------------------< ItemStateListener > /** * {@inheritDoc}