You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/02/26 21:51:51 UTC
svn commit: r155583 [1/2] - in incubator/jackrabbit/trunk:
applications/test/ src/java/org/apache/jackrabbit/core/
src/java/org/apache/jackrabbit/core/observation/
src/java/org/apache/jackrabbit/core/state/
src/java/org/apache/jackrabbit/core/version/persistence/
src/test/org/apache/jackrabbit/test/
src/test/org/apache/jackrabbit/test/api/observation/
src/test/org/apache/jackrabbit/test/api/version/
Author: mreutegg
Date: Sat Feb 26 12:51:46 2005
New Revision: 155583
URL: http://svn.apache.org/viewcvs?view=rev&rev=155583
Log:
- Redesigned observation to also include workspace modifications
- Extended observation tests and moved them into the api.observation package
Added:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/AbstractObservationTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/AddEventListenerTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/EventIteratorTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/EventResult.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/EventTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/GetRegisteredEventListenersTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/LockingTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/NodeAddedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/NodeMovedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/NodeRemovedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/PropertyAddedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/PropertyChangedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/PropertyRemovedTest.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/WorkspaceOperationTest.java (with props)
Modified:
incubator/jackrabbit/trunk/applications/test/repositoryStubImpl.properties
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventConsumer.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventFilter.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventState.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/AbstractJCRTest.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/observation/TestAll.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/version/AbstractVersionTest.java
Modified: incubator/jackrabbit/trunk/applications/test/repositoryStubImpl.properties
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/applications/test/repositoryStubImpl.properties?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/applications/test/repositoryStubImpl.properties (original)
+++ incubator/jackrabbit/trunk/applications/test/repositoryStubImpl.properties Sat Feb 26 12:51:46 2005
@@ -21,10 +21,10 @@
# global test configuration
javax.jcr.tck.testroot=/testroot
javax.jcr.tck.nodetype=nt:unstructured
-javax.jcr.tck.nodename1=foo
-javax.jcr.tck.nodename2=bar
-javax.jcr.tck.nodename3=foobar
-javax.jcr.tck.nodename4=myname
+javax.jcr.tck.nodename1=node1
+javax.jcr.tck.nodename2=node2
+javax.jcr.tck.nodename3=node3
+javax.jcr.tck.nodename4=node4
javax.jcr.tck.propertyname1=prop1
javax.jcr.tck.propertyname2=prop2
javax.jcr.tck.workspacename=test
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/HierarchyManagerImpl.java Sat Feb 26 12:51:46 2005
@@ -289,7 +289,11 @@
// to the same child node, always use the first one
NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) entries.get(0);
// add to path
- builder.addFirst(entry.getName(), entry.getIndex());
+ if (entry.getIndex() == 1) {
+ builder.addFirst(entry.getName());
+ } else {
+ builder.addFirst(entry.getName(), entry.getIndex());
+ }
} else {
PropertyState propState = (PropertyState) state;
QName name = propState.getName();
@@ -538,7 +542,11 @@
// get a path builder clone from the tail of the queue
Path.PathBuilder pb = (Path.PathBuilder) queue.removeLast();
// add entry to path
- pb.addFirst(entry.getName(), entry.getIndex());
+ if (entry.getIndex() == 1) {
+ pb.addFirst(entry.getName());
+ } else {
+ pb.addFirst(entry.getName(), entry.getIndex());
+ }
// recurse
recursiveBuildPaths(new NodeId(parentUUID), pb, builders, includeZombies);
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java Sat Feb 26 12:51:46 2005
@@ -20,8 +20,6 @@
import org.apache.jackrabbit.core.nodetype.NodeDefImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.core.nodetype.PropertyDefImpl;
-import org.apache.jackrabbit.core.observation.EventStateCollection;
-import org.apache.jackrabbit.core.observation.ObservationManagerFactory;
import org.apache.jackrabbit.core.security.AccessManager;
import org.apache.jackrabbit.core.state.ItemState;
import org.apache.jackrabbit.core.state.ItemStateException;
@@ -1157,13 +1155,6 @@
*/
validateTransientItems(dirty.iterator());
- WorkspaceImpl wsp = (WorkspaceImpl) session.getWorkspace();
-
- // list of events that are generated by saved changes
- ObservationManagerFactory obsFactory = rep.getObservationManagerFactory(wsp.getName());
- EventStateCollection events = obsFactory.createEventStateCollection(session,
- session.getItemStateManager(), session.getHierarchyManager());
-
/**
* build list of transient descendents in the attic
* (i.e. those marked as 'removed')
@@ -1178,17 +1169,6 @@
Collection dirtyRefs =
checkReferences(dirty.iterator(), removed.iterator());
- /**
- * create event states for the affected item states and
- * prepare them for event dispatch (this step is necessary in order
- * to check access rights on items that will be removed)
- *
- * todo consolidate event generating and dispatching code (ideally one method call after save has succeeded)
- */
- events.createEventStates(removed);
- events.createEventStates(dirty);
- events.prepare();
-
try {
// start the update operation
stateMgr.edit();
@@ -1208,6 +1188,20 @@
// process 'new' or 'modified' transient states
persistTransientItems(dirty.iterator());
+ // dispose the transient states marked 'new' or 'modified'
+ // at this point item state data is pushed down one level,
+ // node instances are disconnected from the transient
+ // item state and connected to the 'overlayed' item state.
+ // transient item states must be removed now. otherwise
+ // the session item state provider will return an orphaned
+ // item state which is not referenced by any node instance.
+ iter = dirty.iterator();
+ while (iter.hasNext()) {
+ transientState = (ItemState) iter.next();
+ // dispose the transient state, it is no longer used
+ stateMgr.disposeTransientItemState(transientState);
+ }
+
// store the references calculated above
for (Iterator it = dirtyRefs.iterator(); it.hasNext();) {
stateMgr.store((NodeReferences) it.next());
@@ -1225,28 +1219,16 @@
}
// now it is safe to dispose the transient states:
- // dispose the transient states marked 'removed'
+ // dispose the transient states marked 'removed'.
+ // item states in attic are removed after store, because
+ // the observation mechanism needs to build paths of removed
+ // items in store().
iter = removed.iterator();
while (iter.hasNext()) {
transientState = (ItemState) iter.next();
// dispose the transient state, it is no longer used
stateMgr.disposeTransientItemStateInAttic(transientState);
}
- // dispose the transient states marked 'new' or 'modified'
- iter = dirty.iterator();
- while (iter.hasNext()) {
- transientState = (ItemState) iter.next();
- // dispose the transient state, it is no longer used
- stateMgr.disposeTransientItemState(transientState);
- }
-
- /**
- * all changes are persisted, now dispatch events;
- * forward this to the session to let it decide on the right
- * time for those events to be dispatched in case of
- * transactional support
- */
- session.dispatch(events);
} finally {
// turn off temporary path caching
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java Sat Feb 26 12:51:46 2005
@@ -36,6 +36,7 @@
import org.apache.jackrabbit.core.state.UpdatableItemStateManager;
import org.apache.jackrabbit.core.util.uuid.UUID;
import org.apache.jackrabbit.core.xml.ImportHandler;
+import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
import org.apache.log4j.Logger;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
@@ -103,7 +104,7 @@
/**
* The <code>ObservationManager</code> instance for this session.
*/
- protected ObservationManager obsMgr;
+ protected ObservationManagerImpl obsMgr;
/**
* The <code>QueryManager</code> for this <code>Workspace</code>.
@@ -123,11 +124,13 @@
* @param rep
* @param session
*/
- WorkspaceImpl(WorkspaceConfig wspConfig, SharedItemStateManager stateMgr,
- RepositoryImpl rep, SessionImpl session) {
+ WorkspaceImpl(WorkspaceConfig wspConfig,
+ SharedItemStateManager stateMgr,
+ RepositoryImpl rep,
+ SessionImpl session) {
this.wspConfig = wspConfig;
this.rep = rep;
- this.stateMgr = new TransactionalItemStateManager(stateMgr);
+ this.stateMgr = new TransactionalItemStateManager(stateMgr, this);
this.hierMgr = new HierarchyManagerImpl(rep.getRootNodeUUID(),
this.stateMgr, session.getNamespaceResolver());
this.session = session;
@@ -933,7 +936,16 @@
/**
* @see Workspace#getObservationManager
*/
- public synchronized ObservationManager getObservationManager()
+ public ObservationManager getObservationManager()
+ throws UnsupportedRepositoryOperationException, RepositoryException {
+ return getObservationManagerImpl();
+ }
+
+ /**
+ * Returns the ObservationManagerImpl for this workspace instance.
+ * @return the ObservationManagerImpl for this workspace instance.
+ */
+ public synchronized ObservationManagerImpl getObservationManagerImpl()
throws UnsupportedRepositoryOperationException, RepositoryException {
// check state of this instance
@@ -941,7 +953,7 @@
if (obsMgr == null) {
try {
- obsMgr = rep.getObservationManagerFactory(wspConfig.getName()).createObservationManager(session, session.getItemManager());
+ obsMgr = rep.getObservationManagerFactory(wspConfig.getName()).createObservationManager(session, session.hierMgr, session.getItemManager());
} catch (NoSuchWorkspaceException nswe) {
// should never get here
String msg = "internal error: failed to instantiate observation manager";
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventConsumer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventConsumer.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventConsumer.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventConsumer.java Sat Feb 26 12:51:46 2005
@@ -130,10 +130,8 @@
* enough access rights to see the event.
*
* @param events the collection of {@link EventState}s.
- * @throws RepositoryException if an error occurs while checking access
- * rights on the {@link EventStateCollection}.
*/
- void prepareEvents(EventStateCollection events) throws RepositoryException {
+ void prepareEvents(EventStateCollection events) {
Iterator it = events.iterator();
Set denied = null;
while (it.hasNext()) {
@@ -151,12 +149,18 @@
ItemId targetId;
if (state.getChildUUID() == null) {
// target is a property
- targetId = new PropertyId(state.getParentUUID(), state.getChildRelPath().getElements()[0].getName());
+ targetId = new PropertyId(state.getParentUUID(), state.getChildRelPath().getName());
} else {
// target is a node
targetId = new NodeId(state.getChildUUID());
}
- if (!session.getAccessManager().isGranted(targetId, AccessManager.READ)) {
+ boolean granted = false;
+ try {
+ granted = session.getAccessManager().isGranted(targetId, AccessManager.READ);
+ } catch (RepositoryException e) {
+ log.warn("Unable to check access rights for item: " + targetId);
+ }
+ if (!granted) {
if (denied == null) {
denied = new HashSet();
}
@@ -186,7 +190,7 @@
ItemId targetId;
if (state.getChildUUID() == null) {
// target is a property
- targetId = new PropertyId(state.getParentUUID(), state.getChildRelPath().getElements()[0].getName());
+ targetId = new PropertyId(state.getParentUUID(), state.getChildRelPath().getName());
} else {
// target is a node
targetId = new NodeId(state.getChildUUID());
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventFilter.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventFilter.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventFilter.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventFilter.java Sat Feb 26 12:51:46 2005
@@ -21,8 +21,10 @@
import org.apache.jackrabbit.core.Path;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
+import org.apache.log4j.Logger;
import javax.jcr.RepositoryException;
+import javax.jcr.observation.Event;
/**
* The <code>EventFilter</code> class implements the filter logic based
@@ -30,6 +32,11 @@
*/
class EventFilter {
+ /**
+ * Logger instance for this class.
+ */
+ private static final Logger log = Logger.getLogger(EventFilter.class);
+
static final EventFilter BLOCK_ALL = new BlockAllFilter();
/**
@@ -173,21 +180,11 @@
}
}
- /*
- Node parent = null;
- try {
- parent = (Node) itemMgr.getItem(new NodeId(eventState.getParentUUID()));
- } catch (AccessDeniedException e) {
- log.debug("Access denied for " + eventState.getParentPath());
- return true;
- }
- */
-
// check node types
if (nodeTypes != null) {
boolean match = false;
for (int i = 0; i < nodeTypes.length && !match; i++) {
- match |= eventState.getNodeType().isDerivedFrom(nodeTypes[i].getQName());
+ match |= eventState.getNodeType().equals(nodeTypes[i]) || eventState.getNodeType().isDerivedFrom(nodeTypes[i].getQName());
}
if (!match) {
return true;
@@ -196,10 +193,25 @@
// finally check path
try {
- //parentPath = Path.create(parent.getPath(), session.getNamespaceResolver(), false);
- boolean match = eventState.getParentPath().equals(path);
+ // the relevant path for the path filter depends on the event type
+ // for node events, the relevant path is the one returned by
+ // Event.getPath().
+ // for property events, the relevant path is the path of the
+ // node where the property belongs to.
+ Path eventPath = null;
+ if (type == Event.NODE_ADDED || type == Event.NODE_REMOVED) {
+ Path.PathElement nameElem = eventState.getChildRelPath();
+ if (nameElem.getIndex() == 0) {
+ eventPath = Path.create(eventState.getParentPath(), nameElem.getName(), false);
+ } else {
+ eventPath = Path.create(eventState.getParentPath(), nameElem.getName(), nameElem.getIndex(), false);
+ }
+ } else {
+ eventPath = eventState.getParentPath();
+ }
+ boolean match = eventPath.equals(path);
if (!match && isDeep) {
- match = eventState.getParentPath().isDescendantOf(path);
+ match = eventPath.isDescendantOf(path);
}
return !match;
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventImpl.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventImpl.java Sat Feb 26 12:51:46 2005
@@ -81,7 +81,12 @@
*/
public String getPath() throws RepositoryException {
try {
- Path p = Path.create(eventState.getParentPath(), eventState.getChildRelPath(), false);
+ Path p = null;
+ if (eventState.getChildRelPath().getIndex() > 0) {
+ p = Path.create(eventState.getParentPath(), eventState.getChildRelPath().getName(), eventState.getChildRelPath().getIndex(), false);
+ } else {
+ p = Path.create(eventState.getParentPath(), eventState.getChildRelPath().getName(), false);
+ }
return p.toJCRPath(session.getNamespaceResolver());
} catch (MalformedPathException e) {
String msg = "internal error: malformed path for event";
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventState.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventState.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventState.java Sat Feb 26 12:51:46 2005
@@ -54,7 +54,7 @@
* The relative path of the child item associated with this event.
* This is basically the name of the item with an optional index.
*/
- private final Path childRelPath;
+ private final Path.PathElement childRelPath;
/**
* The node type of the parent node.
@@ -97,7 +97,7 @@
String parentUUID,
Path parentPath,
String childUUID,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
int mask = (Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED);
@@ -138,7 +138,7 @@
public static EventState childNodeAdded(String parentUUID,
Path parentPath,
String childUUID,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
return new EventState(Event.NODE_ADDED,
@@ -167,7 +167,7 @@
public static EventState childNodeRemoved(String parentUUID,
Path parentPath,
String childUUID,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
return new EventState(Event.NODE_REMOVED,
@@ -194,7 +194,7 @@
*/
public static EventState propertyAdded(String parentUUID,
Path parentPath,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
return new EventState(Event.PROPERTY_ADDED,
@@ -221,7 +221,7 @@
*/
public static EventState propertyRemoved(String parentUUID,
Path parentPath,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
return new EventState(Event.PROPERTY_REMOVED,
@@ -248,7 +248,7 @@
*/
public static EventState propertyChanged(String parentUUID,
Path parentPath,
- Path childPath,
+ Path.PathElement childPath,
NodeTypeImpl nodeType,
Session session) {
return new EventState(Event.PROPERTY_CHANGED,
@@ -300,9 +300,9 @@
* Returns the relative {@link Path} of the child
* {@link javax.jcr.Item} associated with this event.
*
- * @return the <code>Path</code> associated with this event.
+ * @return the <code>Path.PathElement</code> associated with this event.
*/
- public Path getChildRelPath() {
+ public Path.PathElement getChildRelPath() {
return childRelPath;
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java Sat Feb 26 12:51:46 2005
@@ -16,16 +16,27 @@
*/
package org.apache.jackrabbit.core.observation;
-import org.apache.jackrabbit.core.*;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
-import org.apache.jackrabbit.core.state.*;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.MalformedPathException;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.ItemStateManager;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
import org.apache.log4j.Logger;
import javax.jcr.RepositoryException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Arrays;
/**
* The <code>EventStateCollection</code> class implements how {@link EventState}
@@ -55,11 +66,6 @@
private final SessionImpl session;
/**
- * The ItemStateProvider of the session that creates the events.
- */
- private final ItemStateManager provider;
-
- /**
* The HierarchyManager of the session that creates the events.
*/
private final HierarchyManager hmgr;
@@ -71,134 +77,335 @@
*/
EventStateCollection(ObservationManagerFactory dispatcher,
SessionImpl session,
- ItemStateManager provider,
HierarchyManager hmgr) {
this.dispatcher = dispatcher;
this.session = session;
- this.provider = provider;
this.hmgr = hmgr;
}
/**
- * Creates {@link EventState}s for the {@link org.apache.jackrabbit.core.state.ItemState state}
- * instances contained in the specified collection.
- *
- * @param states collection of transient <code>ItemState</code> for whom
- * to create {@link EventState}s.
- * @see #createEventStates(org.apache.jackrabbit.core.state.ItemState)
- */
- public void createEventStates(Collection states)
- throws RepositoryException {
- Iterator iter = states.iterator();
- while (iter.hasNext()) {
- createEventStates((ItemState) iter.next());
- }
- }
-
- /**
- * Creates {@link EventState}s for the passed {@link org.apache.jackrabbit.core.state.ItemState state}
- * instance.
- *
- * @param state the transient <code>ItemState</code> for whom
- * to create {@link EventState}s.
+ * Creates {@link EventState} instances from <code>ItemState</code>
+ * <code>changes</code>.
+ * @param changes the changes on <code>ItemState</code>s.
+ * @param provider an <code>ItemStateProvider</code> to provide <code>ItemState</code>
+ * of items that are not contained in the <code>changes</code> collection.
+ * @throws ItemStateException if an error occurs while creating events
+ * states for the item state changes.
*/
- public void createEventStates(ItemState state)
- throws RepositoryException {
- int status = state.getStatus();
-
- if (status == ItemState.STATUS_EXISTING_MODIFIED
- || status == ItemState.STATUS_NEW) {
-
+ public void createEventStates(ChangeLog changes, ItemStateManager provider) throws ItemStateException {
+ for (Iterator it = changes.modifiedStates(); it.hasNext();) {
+ ItemState state = (ItemState) it.next();
if (state.isNode()) {
- NodeState currentNode = (NodeState) state;
- QName nodeTypeName = currentNode.getNodeTypeName();
- NodeTypeImpl nodeType = session.getNodeTypeManager().getNodeType(nodeTypeName);
- Path parentPath = hmgr.getPath(currentNode.getId());
-
- // 1) check added properties
- List addedProperties = currentNode.getAddedPropertyEntries();
- for (Iterator it = addedProperties.iterator(); it.hasNext();) {
- NodeState.PropertyEntry prop = (NodeState.PropertyEntry) it.next();
- events.add(EventState.propertyAdded(currentNode.getUUID(),
- parentPath,
- Path.create(prop.getName(), 0),
- nodeType,
- session));
- }
-
- // 2) check removed properties
- List removedProperties = currentNode.getRemovedPropertyEntries();
- for (Iterator it = removedProperties.iterator(); it.hasNext();) {
- NodeState.PropertyEntry prop = (NodeState.PropertyEntry) it.next();
- events.add(EventState.propertyRemoved(currentNode.getUUID(),
- parentPath,
- Path.create(prop.getName(), 0),
- nodeType,
- session));
- }
-
- // 3) check for added nodes
- List addedNodes = currentNode.getAddedChildNodeEntries();
- for (Iterator it = addedNodes.iterator(); it.hasNext();) {
- NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry) it.next();
- events.add(EventState.childNodeAdded(currentNode.getUUID(),
- parentPath,
- child.getUUID(),
- Path.create(child.getName(), child.getIndex()),
- nodeType,
- session));
- }
-
- // 4) check for removed nodes
- List removedNodes = currentNode.getRemovedChildNodeEntries();
- for (Iterator it = removedNodes.iterator(); it.hasNext();) {
- NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry) it.next();
- events.add(EventState.childNodeRemoved(currentNode.getUUID(),
- parentPath,
- child.getUUID(),
- Path.create(child.getName(), child.getIndex()),
- nodeType,
- session));
- }
- } else {
- // only add property changed event if property is existing
- if (state.getStatus() == ItemState.STATUS_EXISTING_MODIFIED) {
- NodeId parentId = new NodeId(state.getParentUUID());
+ // node changed
+ // covers the following cases:
+ // 1) property added
+ // 2) property removed
+ // 3) child node added
+ // 4) child node removed
+ // 5) node moved
+ // cases 1) and 2) are detected with added and deleted states
+ // on the PropertyState itself.
+ // cases 3) and 4) are detected with added and deleted states
+ // on the NodeState itself.
+ // in case 5) two or three nodes change. two nodes are changed
+ // when a child node is renamed. three nodes are changed when
+ // a node is really moved. In any case we are only interested in
+ // the node that actually got moved.
+ NodeState n = (NodeState) state;
+ if (n.getAddedParentUUIDs().size() > 0 && n.getRemovedParentUUIDs().size() > 0) {
+ // node moved
+ // generate node removed & node added event
+ String oldParentUUID = (String) n.getRemovedParentUUIDs().get(0);
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ // FIXME find more efficient way
+ Path newPath = null;
+ Path[] allPaths = null;
try {
- NodeState parentState = (NodeState) provider.getItemState(parentId);
- Path parentPath = hmgr.getPath(parentId);
- events.add(EventState.propertyChanged(state.getParentUUID(),
+ newPath = hmgr.getPath(n.getId());
+ allPaths = hmgr.getAllPaths(n.getId(), true);
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + n.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ List paths = new ArrayList(Arrays.asList(allPaths));
+ paths.remove(newPath);
+ if (paths.size() > 0) {
+ Path removedPath = (Path) paths.get(0);
+ Path parentPath = null;
+ try {
+ parentPath = removedPath.getAncestor(1);
+ } catch (PathNotFoundException e) {
+ // should never happen actually, root node cannot
+ // be removed, thus path has always a parent
+ String msg = "Path " + removedPath + " has no parent";
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.childNodeRemoved(oldParentUUID,
parentPath,
- Path.create(((PropertyState) state).getName(), 0),
- session.getNodeTypeManager().getNodeType(parentState.getNodeTypeName()),
+ n.getUUID(),
+ removedPath.getNameElement(),
+ nodeType,
session));
- } catch (ItemStateException e) {
- // should never happen
- log.error("internal error: item state exception", e);
+
+ String newParentUUID = (String) n.getAddedParentUUIDs().get(0);
+ try {
+ parentPath = newPath.getAncestor(1);
+ } catch (PathNotFoundException e) {
+ // should never happen actually, root node cannot
+ // be 'added', thus path has always a parent
+ String msg = "Path " + removedPath + " has no parent";
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.childNodeAdded(newParentUUID,
+ parentPath,
+ n.getUUID(),
+ newPath.getNameElement(),
+ nodeType,
+ session));
+ } else {
+ log.error("Unable to calculate old path of moved node");
+ }
+ } else {
+ // a moved node always has a modified parent node
+ NodeState parent = null;
+ try {
+ // root node does not have a parent UUID
+ if (state.getParentUUID() != null) {
+ parent = (NodeState) changes.get(new NodeId(state.getParentUUID()));
+ }
+ } catch (NoSuchItemStateException e) {
+ // should never happen actually. this would mean
+ // the parent of this modified node is deleted
+ String msg = "Parent of node " + state.getId() + " is deleted.";
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ if (parent != null) {
+ // check if node has been renamed
+ NodeState.ChildNodeEntry moved = null;
+ for (Iterator removedNodes = parent.getRemovedChildNodeEntries().iterator(); removedNodes.hasNext();) {
+ NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry) removedNodes.next();
+ if (child.getUUID().equals(n.getUUID())) {
+ // found node re-added with different name
+ moved = child;
+ }
+ }
+ if (moved != null) {
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ Path newPath = null;
+ Path parentPath = null;
+ Path oldPath = null;
+ try {
+ newPath = hmgr.getPath(state.getId());
+ parentPath = newPath.getAncestor(1);
+ oldPath = null;
+ if (moved.getIndex() == 0) {
+ oldPath = Path.create(parentPath, moved.getName(), false);
+ } else {
+ oldPath = Path.create(parentPath, moved.getName(), moved.getIndex(), false);
+ }
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + state.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ } catch (MalformedPathException e) {
+ // should never happen actually
+ String msg = "Malformed path for item: " + state.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.childNodeRemoved(parent.getUUID(),
+ parentPath,
+ n.getUUID(),
+ oldPath.getNameElement(),
+ nodeType,
+ session));
+ events.add(EventState.childNodeAdded(parent.getUUID(),
+ parentPath,
+ n.getUUID(),
+ newPath.getNameElement(),
+ nodeType,
+ session));
+ }
}
}
+ } else {
+ // property changed
+ Path path = null;
+ Path parentPath = null;
+ try {
+ path = hmgr.getPath(state.getId());
+ parentPath = path.getAncestor(1);
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + state.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ NodeState parent = (NodeState) provider.getItemState(new NodeId(state.getParentUUID()));
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(parent.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + parent.getId() + " has unknown node type: " + parent.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.propertyChanged(state.getParentUUID(),
+ parentPath,
+ path.getNameElement(),
+ nodeType,
+ session));
+ }
+ }
+ for (Iterator it = changes.addedStates(); it.hasNext();) {
+ ItemState state = (ItemState) it.next();
+ if (state.isNode()) {
+ // node created
+ NodeState n = (NodeState) state;
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ Path path = null;
+ Path parentPath = null;
+ try {
+ path = hmgr.getPath(n.getId());
+ parentPath = path.getAncestor(1);
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + n.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.childNodeAdded(n.getParentUUID(),
+ parentPath,
+ n.getUUID(),
+ path.getNameElement(),
+ nodeType,
+ session));
+ } else {
+ // property created / set
+ NodeState n = (NodeState) changes.get(new NodeId(state.getParentUUID()));
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ Path path = null;
+ Path parentPath = null;
+ try {
+ path = hmgr.getPath(state.getId());
+ parentPath = path.getAncestor(1);
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + n.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ events.add(EventState.propertyAdded(state.getParentUUID(),
+ parentPath,
+ path.getNameElement(),
+ nodeType,
+ session));
}
- } else if (status == ItemState.STATUS_EXISTING_REMOVED) {
+ }
+ for (Iterator it = changes.deletedStates(); it.hasNext();) {
+ ItemState state = (ItemState) it.next();
if (state.isNode()) {
- // zombie nodes
- NodeState currentNode = (NodeState) state;
- QName nodeTypeName = currentNode.getNodeTypeName();
- NodeTypeImpl nodeType = session.getNodeTypeManager().getNodeType(nodeTypeName);
-
- // FIXME replace by HierarchyManager.getPath(ItemId id, boolean includeZombie)
- // when available.
- Path[] parentPaths = hmgr.getAllPaths(currentNode.getId(), true); // include zombie
- for (int i = 0; i < parentPaths.length; i++) {
- List removedNodes = currentNode.getRemovedChildNodeEntries();
- for (Iterator it = removedNodes.iterator(); it.hasNext();) {
- NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry) it.next();
- events.add(EventState.childNodeRemoved(currentNode.getUUID(),
- parentPaths[i],
- child.getUUID(),
- Path.create(child.getName(), child.getIndex()),
+ // node deleted
+ NodeState n = (NodeState) state;
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ try {
+ Path[] paths = hmgr.getAllPaths(state.getId(), true);
+ for (int i = 0; i < paths.length; i++) {
+ Path parentPath = paths[i].getAncestor(1);
+ events.add(EventState.childNodeRemoved(n.getParentUUID(),
+ parentPath,
+ n.getUUID(),
+ paths[i].getNameElement(),
nodeType,
session));
}
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + n.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ } else {
+ // property removed
+ // only create an event if node still exists
+ try {
+ NodeState n = (NodeState) changes.get(new NodeId(state.getParentUUID()));
+ // node state exists -> only property removed
+ NodeTypeImpl nodeType = null;
+ try {
+ nodeType = session.getNodeTypeManager().getNodeType(n.getNodeTypeName());
+ } catch (NoSuchNodeTypeException e) {
+ // should never happen actually
+ String msg = "Item " + state.getId() + " has unknown node type: " + n.getNodeTypeName();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ Path paths[] = null;
+ try {
+ paths = hmgr.getAllPaths(state.getId(), true);
+ for (int i = 0; i < paths.length; i++) {
+ Path parentPath = paths[i].getAncestor(1);
+ events.add(EventState.propertyRemoved(state.getParentUUID(),
+ parentPath,
+ paths[i].getNameElement(),
+ nodeType,
+ session));
+ }
+ } catch (RepositoryException e) {
+ // should never happen actually
+ String msg = "Unable to resolve path for item: " + n.getId();
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
+ } catch (NoSuchItemStateException e) {
+ // also node removed -> do not create an event
}
}
}
@@ -207,7 +414,7 @@
/**
* Prepares the events for dispatching.
*/
- public void prepare() throws RepositoryException {
+ public void prepare() {
dispatcher.prepareEvents(this);
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java Sat Feb 26 12:51:46 2005
@@ -19,16 +19,11 @@
import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.BufferUtils;
import org.apache.commons.collections.UnboundedFifoBuffer;
-import org.apache.jackrabbit.core.*;
-import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
-import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
-import org.apache.jackrabbit.core.state.ItemStateManager;
import org.apache.log4j.Logger;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.core.ItemManager;
-import javax.jcr.RepositoryException;
-import javax.jcr.observation.EventListener;
-import javax.jcr.observation.EventListenerIterator;
-import javax.jcr.observation.ObservationManager;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -119,7 +114,7 @@
*
* @return <code>Set</code> of <code>EventConsumer</code>s.
*/
- private Set getAsynchronousConsumers() {
+ Set getAsynchronousConsumers() {
synchronized (consumerChange) {
if (readOnlyConsumers == null) {
readOnlyConsumers = Collections.unmodifiableSet(new HashSet(activeConsumers));
@@ -128,7 +123,7 @@
}
}
- private Set getSynchronousConsumers() {
+ Set getSynchronousConsumers() {
synchronized (consumerChange) {
if (synchronousReadOnlyConsumers == null) {
synchronousReadOnlyConsumers = Collections.unmodifiableSet(new HashSet(synchronousConsumers));
@@ -145,15 +140,10 @@
* @param itemMgr the <code>ItemManager</code> of the <code>session</code>.
* @return an <code>ObservationManager</code>.
*/
- public ObservationManager createObservationManager(SessionImpl session,
- ItemManager itemMgr) {
- return new SessionLocalObservationManager(session, itemMgr);
- }
-
- public EventStateCollection createEventStateCollection(SessionImpl session,
- ItemStateManager provider,
- HierarchyManager hmgr) {
- return new EventStateCollection(this, session, provider, hmgr);
+ public ObservationManagerImpl createObservationManager(SessionImpl session,
+ HierarchyManager hmgr,
+ ItemManager itemMgr) {
+ return new ObservationManagerImpl(this, session, hmgr, itemMgr);
}
/**
@@ -187,8 +177,7 @@
*
* @param events the {@link EventState}s to prepare.
*/
- void prepareEvents(EventStateCollection events)
- throws RepositoryException {
+ void prepareEvents(EventStateCollection events) {
Set consumers = new HashSet();
consumers.addAll(getSynchronousConsumers());
consumers.addAll(getAsynchronousConsumers());
@@ -222,142 +211,46 @@
eventQueue.add(new DispatchAction(events, getAsynchronousConsumers()));
}
- //----------------------------< adapter class >-----------------------------
-
/**
- * Each <code>Session</code> instance has its own <code>ObservationManager</code>
- * instance. The class <code>SessionLocalObservationManager</code> implements
- * this behaviour.
- */
- class SessionLocalObservationManager implements ObservationManager {
-
- /**
- * The <code>Session</code> this <code>ObservationManager</code>
- * belongs to.
- */
- private SessionImpl session;
-
- /**
- * The <code>ItemManager</code> for this <code>ObservationManager</code>.
- */
- private ItemManager itemMgr;
-
- /**
- * Creates an <code>ObservationManager</code> instance.
- *
- * @param session the <code>Session</code> this ObservationManager
- * belongs to.
- * @param itemMgr {@link org.apache.jackrabbit.core.ItemManager} of the passed
- * <code>Session</code>.
- * @throws NullPointerException if <code>session</code> or <code>itemMgr</code>
- * is <code>null</code>.
- */
- SessionLocalObservationManager(SessionImpl session,
- ItemManager itemMgr) {
- if (session == null) {
- throw new NullPointerException("session");
- }
- if (itemMgr == null) {
- throw new NullPointerException("itemMgr");
- }
-
- this.session = session;
- this.itemMgr = itemMgr;
- }
-
- /**
- * @see ObservationManager#addEventListener
- */
- public void addEventListener(EventListener listener,
- int eventTypes,
- String absPath,
- boolean isDeep,
- String[] uuid,
- String[] nodeTypeName,
- boolean noLocal)
- throws RepositoryException {
-
- // create NodeType instances from names
- NodeTypeImpl[] nodeTypes;
- if (nodeTypeName == null) {
- nodeTypes = null;
+ * Adds or replaces an event consumer.
+ * @param consumer the <code>EventConsumer</code> to add or replace.
+ */
+ void addConsumer(EventConsumer consumer) {
+ synchronized (consumerChange) {
+ if (consumer.getEventListener() instanceof SynchronousEventListener) {
+ // remove existing if any
+ synchronousConsumers.remove(consumer);
+ // re-add it
+ synchronousConsumers.add(consumer);
+ // reset read only consumer set
+ synchronousReadOnlyConsumers = null;
} else {
- NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
- nodeTypes = new NodeTypeImpl[nodeTypeName.length];
- for (int i = 0; i < nodeTypes.length; i++) {
- nodeTypes[i] = (NodeTypeImpl) ntMgr.getNodeType(nodeTypeName[i]);
- }
- }
-
- Path path;
- try {
- path = Path.create(absPath, session.getNamespaceResolver(), true);
- } catch (MalformedPathException mpe) {
- String msg = "invalid path syntax: " + absPath;
- log.debug(msg);
- throw new RepositoryException(msg, mpe);
- }
- // create filter
- EventFilter filter = new EventFilter(itemMgr,
- session,
- eventTypes,
- path,
- isDeep,
- uuid,
- nodeTypes,
- noLocal);
-
- EventConsumer consumer =
- new EventConsumer(session, listener, filter);
-
- synchronized (consumerChange) {
- if (listener instanceof SynchronousEventListener) {
- // remove existing if any
- synchronousConsumers.remove(consumer);
- // re-add it
- synchronousConsumers.add(consumer);
- // reset read only consumer set
- synchronousReadOnlyConsumers = null;
- } else {
- // remove existing if any
- activeConsumers.remove(consumer);
- // re-add it
- activeConsumers.add(consumer);
- // reset read only consumer set
- readOnlyConsumers = null;
- }
+ // remove existing if any
+ activeConsumers.remove(consumer);
+ // re-add it
+ activeConsumers.add(consumer);
+ // reset read only consumer set
+ readOnlyConsumers = null;
}
}
+ }
- /**
- * @see ObservationManager#removeEventListener(javax.jcr.observation.EventListener)
- */
- public void removeEventListener(EventListener listener)
- throws RepositoryException {
- EventConsumer consumer =
- new EventConsumer(session, listener, EventFilter.BLOCK_ALL);
-
- synchronized (consumerChange) {
- if (listener instanceof SynchronousEventListener) {
- synchronousConsumers.remove(consumer);
- // reset read only listener set
- synchronousReadOnlyConsumers = null;
- } else {
- activeConsumers.remove(consumer);
- // reset read only listener set
- readOnlyConsumers = null;
- }
+ /**
+ * Unregisters an event consumer from event notification.
+ * @param consumer the consumer to deregister.
+ */
+ void removeConsumer(EventConsumer consumer) {
+ synchronized (consumerChange) {
+ if (consumer.getEventListener() instanceof SynchronousEventListener) {
+ synchronousConsumers.remove(consumer);
+ // reset read only listener set
+ synchronousReadOnlyConsumers = null;
+ } else {
+ activeConsumers.remove(consumer);
+ // reset read only listener set
+ readOnlyConsumers = null;
}
}
-
- /**
- * @see ObservationManager#getRegisteredEventListeners()
- */
- public EventListenerIterator getRegisteredEventListeners()
- throws RepositoryException {
- return new EventListenerIteratorImpl(session,
- getSynchronousConsumers(),
- getAsynchronousConsumers());
- }
}
+
}
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java?view=auto&rev=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java Sat Feb 26 12:51:46 2005
@@ -0,0 +1,165 @@
+/*
+ * $Id: $
+ *
+ * Copyright 1997-2004 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+
+package org.apache.jackrabbit.core.observation;
+
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.ItemManager;
+import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.MalformedPathException;
+import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
+import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
+import org.apache.log4j.Logger;
+
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.EventListenerIterator;
+import javax.jcr.RepositoryException;
+
+/**
+ * Each <code>Session</code> instance has its own <code>ObservationManager</code>
+ * instance. The class <code>SessionLocalObservationManager</code> implements
+ * this behaviour.
+ */
+public class ObservationManagerImpl implements ObservationManager {
+
+ /** The logger instance of this class */
+ private static final Logger log = Logger.getLogger(ObservationManagerImpl.class);
+
+ /**
+ * The <code>Session</code> this <code>ObservationManager</code>
+ * belongs to.
+ */
+ private final SessionImpl session;
+
+ /**
+ * The <code>HierarchyManager</code> of the session.
+ */
+ private final HierarchyManager hmgr;
+
+ /**
+ * The <code>ItemManager</code> for this <code>ObservationManager</code>.
+ */
+ private final ItemManager itemMgr;
+
+ /** The <code>ObservationManagerFactory</code> */
+ private final ObservationManagerFactory obsMgrFactory;
+
+ /**
+ * Creates an <code>ObservationManager</code> instance.
+ *
+ * @param session the <code>Session</code> this ObservationManager
+ * belongs to.
+ * @param hmgr the <code>HierarchyManager</code> of the <code>session</code>.
+ * @param itemMgr {@link org.apache.jackrabbit.core.ItemManager} of the passed
+ * <code>Session</code>.
+ * @throws NullPointerException if <code>session</code> or <code>itemMgr</code>
+ * is <code>null</code>.
+ */
+ ObservationManagerImpl(ObservationManagerFactory obsMgrFactory,
+ SessionImpl session,
+ HierarchyManager hmgr,
+ ItemManager itemMgr) {
+ if (session == null) {
+ throw new NullPointerException("session");
+ }
+ if (itemMgr == null) {
+ throw new NullPointerException("itemMgr");
+ }
+
+ this.obsMgrFactory = obsMgrFactory;
+ this.session = session;
+ this.hmgr = hmgr;
+ this.itemMgr = itemMgr;
+ }
+
+ /**
+ * @see javax.jcr.observation.ObservationManager#addEventListener
+ */
+ public void addEventListener(EventListener listener,
+ int eventTypes,
+ String absPath,
+ boolean isDeep,
+ String[] uuid,
+ String[] nodeTypeName,
+ boolean noLocal)
+ throws RepositoryException {
+
+ // create NodeType instances from names
+ NodeTypeImpl[] nodeTypes;
+ if (nodeTypeName == null) {
+ nodeTypes = null;
+ } else {
+ NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
+ nodeTypes = new NodeTypeImpl[nodeTypeName.length];
+ for (int i = 0; i < nodeTypes.length; i++) {
+ nodeTypes[i] = (NodeTypeImpl) ntMgr.getNodeType(nodeTypeName[i]);
+ }
+ }
+
+ Path path;
+ try {
+ path = Path.create(absPath, session.getNamespaceResolver(), true);
+ } catch (MalformedPathException mpe) {
+ String msg = "invalid path syntax: " + absPath;
+ log.debug(msg);
+ throw new RepositoryException(msg, mpe);
+ }
+ // create filter
+ EventFilter filter = new EventFilter(itemMgr,
+ session,
+ eventTypes,
+ path,
+ isDeep,
+ uuid,
+ nodeTypes,
+ noLocal);
+
+ EventConsumer consumer =
+ new EventConsumer(session, listener, filter);
+ obsMgrFactory.addConsumer(consumer);
+ }
+
+ /**
+ * @see javax.jcr.observation.ObservationManager#removeEventListener(javax.jcr.observation.EventListener)
+ */
+ public void removeEventListener(EventListener listener)
+ throws RepositoryException {
+ EventConsumer consumer =
+ new EventConsumer(session, listener, EventFilter.BLOCK_ALL);
+ obsMgrFactory.removeConsumer(consumer);
+
+ }
+
+ /**
+ * @see javax.jcr.observation.ObservationManager#getRegisteredEventListeners()
+ */
+ public EventListenerIterator getRegisteredEventListeners()
+ throws RepositoryException {
+ return new EventListenerIteratorImpl(session,
+ obsMgrFactory.getSynchronousConsumers(),
+ obsMgrFactory.getAsynchronousConsumers());
+ }
+
+ /**
+ * Creates an <code>EventStateCollection</code> tied to the session
+ * which is attached this <code>ObservationManager</code> instance.
+ * @return a new <code>EventStateCollection</code>.
+ */
+ public EventStateCollection createEventStateCollection() {
+ return new EventStateCollection(obsMgrFactory, session, hmgr);
+ }
+
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java Sat Feb 26 12:51:46 2005
@@ -20,8 +20,10 @@
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.PropertyId;
import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.WorkspaceImpl;
import org.apache.log4j.Logger;
+import javax.jcr.RepositoryException;
import java.io.PrintStream;
/**
@@ -42,6 +44,11 @@
protected final SharedItemStateManager sharedStateMgr;
/**
+ * Local WorkspaceImpl instance.
+ */
+ protected final WorkspaceImpl wspImpl;
+
+ /**
* Flag indicating whether this item state manager is in edit mode
*/
private boolean editMode;
@@ -53,11 +60,18 @@
/**
* Creates a new <code>LocalItemStateManager</code> instance.
+ * todo LocalItemStateManager without a wspImpl will not generate observation events!
*
* @param sharedStateMgr shared state manager
+ * @param wspImpl the workspace instance where this item state manager
+ * belongs to, or <code>null</code> if this item state manager is not
+ * associated with a workspace. This is the case for the version item state
+ * manager. Version item states are not associated with a specific workspace
+ * instance.
*/
- public LocalItemStateManager(SharedItemStateManager sharedStateMgr) {
+ public LocalItemStateManager(SharedItemStateManager sharedStateMgr, WorkspaceImpl wspImpl) {
this.sharedStateMgr = sharedStateMgr;
+ this.wspImpl = wspImpl;
}
/**
@@ -304,7 +318,14 @@
protected void update(ChangeLog changeLog)
throws ItemStateException {
- sharedStateMgr.store(changeLog);
+ try {
+ sharedStateMgr.store(changeLog, (wspImpl != null) ? wspImpl.getObservationManagerImpl() : null);
+ } catch (RepositoryException e) {
+ // should never get here
+ String msg = "ObservationManager unavailable";
+ log.error(msg);
+ throw new ItemStateException(msg, e);
+ }
changeLog.persisted();
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java Sat Feb 26 12:51:46 2005
@@ -17,6 +17,8 @@
package org.apache.jackrabbit.core.state;
import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.nodetype.NodeDefId;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.nodetype.PropDefId;
@@ -293,11 +295,15 @@
* Store modifications registered in a <code>ChangeLog</code>. The items
* contained in the <tt>ChangeLog</tt> are not states returned by this
* item state manager but rather must be reconnected to items provided
- * by this state manager.
+ * by this state manager.<p/>
+ * After successfully storing the states the observation manager is informed
+ * about the changes, if an observation manager is passed to this method.
* @param local change log containing local items
+ * @param obsMgr the observation manager to inform, or <code>null</code> if
+ * no observation manager should be informed.
* @throws ItemStateException if an error occurs
*/
- public synchronized void store(ChangeLog local) throws ItemStateException {
+ public synchronized void store(ChangeLog local, ObservationManagerImpl obsMgr) throws ItemStateException {
ChangeLog shared = new ChangeLog();
/**
@@ -346,6 +352,14 @@
shared.deleted(state.getOverlayedState());
}
+ /* prepare the events */
+ EventStateCollection events = null;
+ if (obsMgr != null) {
+ events = obsMgr.createEventStateCollection();
+ events.createEventStates(local, this);
+ events.prepare();
+ }
+
/* Push all changes from the local items to the shared items */
local.push();
@@ -354,6 +368,11 @@
/* Let the shared item listeners know about the change */
shared.persisted();
+
+ /* dispatch the events */
+ if (events != null) {
+ events.dispatch();
+ }
}
//----------------------------------------------------< ItemStateListener >
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java Sat Feb 26 12:51:46 2005
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.core.state;
import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.WorkspaceImpl;
import org.apache.log4j.Logger;
/**
@@ -37,6 +38,16 @@
private static final String ATTRIBUTE_CHANGE_LOG = "ChangeLog";
/**
+ * ThreadLocal that holds the ChangeLog while this item state manager
+ * is in commit().
+ */
+ private static ThreadLocal commitLog = new ThreadLocal() {
+ protected synchronized Object initialValue() {
+ return new CommitLog();
+ }
+ };
+
+ /**
* Current transactional change log
*/
private transient ChangeLog txLog;
@@ -46,8 +57,8 @@
*
* @param sharedStateMgr shared state manager
*/
- public TransactionalItemStateManager(SharedItemStateManager sharedStateMgr) {
- super(sharedStateMgr);
+ public TransactionalItemStateManager(SharedItemStateManager sharedStateMgr, WorkspaceImpl wspImpl) {
+ super(sharedStateMgr, wspImpl);
}
/**
@@ -75,10 +86,15 @@
ChangeLog changeLog = (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
if (changeLog != null) {
try {
+ // set changeLog in ThreadLocal
+ ((CommitLog) commitLog.get()).setChanges(changeLog);
super.update(changeLog);
} catch (ItemStateException e) {
+ log.error(e);
changeLog.undo(sharedStateMgr);
throw new TransactionException("Unable to end update.", e);
+ } finally {
+ ((CommitLog) commitLog.get()).setChanges(null);
}
changeLog.reset();
tx.notifyCommitted();
@@ -102,13 +118,22 @@
/**
* @see ItemStateManager#getItemState(org.apache.jackrabbit.core.ItemId)
*
- * If associated to a transaction, check our transactional
- * change log first.
+ * If this state manager is committing changes, this method first checks
+ * the commitLog ThreadLocal. Else if associated to a transaction check
+ * the transactional change log. Fallback is always the call to the base
+ * class.
*/
public ItemState getItemState(ItemId id)
throws NoSuchItemStateException, ItemStateException {
- if (txLog != null) {
+ ChangeLog changeLog = ((CommitLog) commitLog.get()).getChanges();
+ if (changeLog != null) {
+ // check items in commit log
+ ItemState state = changeLog.get(id);
+ if (state != null) {
+ return state;
+ }
+ } else if (txLog != null) {
// check items in change log
ItemState state = txLog.get(id);
if (state != null) {
@@ -121,11 +146,24 @@
/**
* @see ItemStateManager#hasItemState(org.apache.jackrabbit.core.ItemId)
*
- * If associated to a transaction, check our transactional
- * change log first.
+ * If this state manager is committing changes, this method first checks
+ * the commitLog ThreadLocal. Else if associated to a transaction check
+ * the transactional change log. Fallback is always the call to the base
+ * class.
*/
public boolean hasItemState(ItemId id) {
- if (txLog != null) {
+ ChangeLog changeLog = ((CommitLog) commitLog.get()).getChanges();
+ if (changeLog != null) {
+ // check items in commit log
+ try {
+ ItemState state = changeLog.get(id);
+ if (state != null) {
+ return true;
+ }
+ } catch (NoSuchItemStateException e) {
+ return false;
+ }
+ } else if (txLog != null) {
// check items in change log
try {
ItemState state = txLog.get(id);
@@ -142,13 +180,22 @@
/**
* @see ItemStateManager#getNodeReferences
*
- * If associated to a transaction, check our transactional
- * change log first.
+ * If this state manager is committing changes, this method first
+ * checks the commitLog ThreadLocal. Else if associated to a transaction
+ * check the transactional change log. Fallback is always the call to
+ * the base class.
*/
public NodeReferences getNodeReferences(NodeReferencesId id)
throws NoSuchItemStateException, ItemStateException {
- if (txLog != null) {
+ ChangeLog changeLog = ((CommitLog) commitLog.get()).getChanges();
+ if (changeLog != null) {
+ // check commit log
+ NodeReferences refs = changeLog.get(id);
+ if (refs != null) {
+ return refs;
+ }
+ } else if (txLog != null) {
// check change log
NodeReferences refs = txLog.get(id);
if (refs != null) {
@@ -170,6 +217,39 @@
txLog.merge(changeLog);
} else {
super.update(changeLog);
+ }
+ }
+
+ //--------------------------< inner classes >-------------------------------
+
+ /**
+ * Helper class that serves as a container for a ChangeLog in a ThreadLocal.
+ * The <code>CommitLog</code> is associated with a <code>ChangeLog</code>
+ * while the <code>TransactionalItemStateManager</code> is in the commit
+ * method.
+ */
+ private static class CommitLog {
+
+ /** The changes that are about to be committed */
+ private ChangeLog changes;
+
+ /**
+ * Sets changes that are about to be committed.
+ * @param changes that are about to be committed, or <code>null</code>
+ * if changes have been committed and the commit log should be reset.
+ */
+ private void setChanges(ChangeLog changes) {
+ this.changes = changes;
+ }
+
+ /**
+ * The changes that are about to be committed, or <code>null</code> if
+ * the <code>TransactionalItemStateManager</code> is currently not
+ * committing any changes.
+ * @return the changes about to be committed.
+ */
+ private ChangeLog getChanges() {
+ return changes;
}
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java Sat Feb 26 12:51:46 2005
@@ -18,6 +18,7 @@
import org.apache.commons.collections.ReferenceMap;
import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.state.*;
import org.apache.jackrabbit.core.util.uuid.UUID;
@@ -165,7 +166,8 @@
//this.stateMgr = new NativeItemStateManager(pMgr, PERSISTENT_ROOT_ID.getUUID(), ntReg);
this.pMgr = pMgr;
SharedItemStateManager sharedStateMgr = new SharedItemStateManager(pMgr, PERSISTENT_ROOT_ID.getUUID(), ntReg);
- stateMgr = new LocalItemStateManager(sharedStateMgr);
+ // todo versioning is not attached to any workspace!! how do we trigger observation?
+ stateMgr = new LocalItemStateManager(sharedStateMgr, null);
NodeState nodeState = (NodeState) stateMgr.getItemState(PERSISTENT_ROOT_ID);
historyRoot = new PersistentNode(stateMgr, nodeState);
initVirtualIds(historyRoot.getState());
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/AbstractJCRTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/AbstractJCRTest.java?view=diff&r1=155582&r2=155583
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/AbstractJCRTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/AbstractJCRTest.java Sat Feb 26 12:51:46 2005
@@ -76,6 +76,16 @@
protected String jcrUUID;
/**
+ * JCR Name jcr:lockOwner using the namespace resolver of the current session.
+ */
+ protected String jcrLockOwner;
+
+ /**
+ * JCR Name jcr:lockIsDeep using the namespace resolver of the current session.
+ */
+ protected String jcrlockIsDeep;
+
+ /**
* JCR Name nt:base using the namespace resolver of the current session.
*/
protected String ntBase;
@@ -212,6 +222,8 @@
jcrPredecessors = superuser.getNamespacePrefix(NS_JCR_URI) + ":predecessors";
jcrBaseVersion = superuser.getNamespacePrefix(NS_JCR_URI) + ":baseVersion";
jcrUUID = superuser.getNamespacePrefix(NS_JCR_URI) + ":uuid";
+ jcrLockOwner = superuser.getNamespacePrefix(NS_JCR_URI) + ":lockOwner";
+ jcrlockIsDeep = superuser.getNamespacePrefix(NS_JCR_URI) + ":lockIsDeep";
ntBase = superuser.getNamespacePrefix(NS_NT_URI) + ":base";
mixReferenceable = superuser.getNamespacePrefix(NS_MIX_URI) + ":referenceable";
mixVersionable = superuser.getNamespacePrefix(NS_MIX_URI) + ":versionable";
@@ -284,6 +296,8 @@
root.save();
}
}
+ } catch (Exception e) {
+ log.println("Exception in tearDown: " + e.toString());
} finally {
superuser.logout();
}