Author: jukka
Date: Wed Jan 29 19:48:47 2014
New Revision: 1562562
URL: http://svn.apache.org/r1562562
Log:
OAK-1332: Large number of changes to the same node can fill observation queue
Break the references from Event instances to the originating event handler,
so that client code that separately queues events won't unnecessarily hold
on to too much memory.
Added:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventQueue.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/QueueingHandler.java
Added:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java?rev=1562562&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java
Wed Jan 29 19:48:47 2014
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.observation;
+
+import static java.util.Collections.emptyMap;
+
+import java.util.Map;
+
+import javax.jcr.observation.Event;
+
+import org.apache.jackrabbit.api.observation.JackrabbitEvent;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Event factory for generating JCR event instances that are optimized
+ * for minimum memory overhead. Each factory instance keeps track of the
+ * event information (like the user identifier and the commit timestamp)
+ * shared across all events from a single commit. The generated events
+ * instances postpone things like path mappings and the construction of
+ * the event info maps to as late as possible to avoid the memory overhead
+ * of keeping track of pre-computed values.
+ */
+class EventFactory {
+
+ private final NamePathMapper mapper;
+
+ private final String userID;
+
+ private final String userData;
+
+ private final long date;
+
+ private final boolean external;
+
+ EventFactory(NamePathMapper mapper, CommitInfo info) {
+ this.mapper = mapper;
+ if (info != null) {
+ this.userID = info.getUserId();
+ this.userData = info.getMessage();
+ this.date = info.getDate();
+ this.external = false;
+ } else {
+ this.userID = CommitInfo.OAK_UNKNOWN;
+ this.userData = null;
+ this.date = System.currentTimeMillis(); // rough estimate
+ this.external = true;
+ }
+ }
+
+ Event propertyAdded(String path, String name, String identifier) {
+ return new EventImpl(path, name, identifier) {
+ @Override
+ public int getType() {
+ return PROPERTY_ADDED;
+ }
+ };
+ }
+
+ Event propertyChanged(String path, String name, String identifier) {
+ return new EventImpl(path, name, identifier) {
+ @Override
+ public int getType() {
+ return PROPERTY_CHANGED;
+ }
+ };
+ }
+
+ Event propertyDeleted(String path, String name, String identifier) {
+ return new EventImpl(path, name, identifier) {
+ @Override
+ public int getType() {
+ return PROPERTY_REMOVED;
+ }
+ };
+ }
+
+ Event nodeAdded(String path, String name, String identifier) {
+ return new EventImpl(path, name, identifier) {
+ @Override
+ public int getType() {
+ return NODE_ADDED;
+ }
+ };
+ }
+
+ Event nodeDeleted(String path, String name, String identifier) {
+ return new EventImpl(path, name, identifier) {
+ @Override
+ public int getType() {
+ return NODE_REMOVED;
+ }
+ };
+ }
+
+ Event nodeMoved(
+ String parent, String name, String identifier,
+ final String sourcePath) {
+ return new EventImpl(parent, name, identifier) {
+ @Override
+ public int getType() {
+ return NODE_MOVED;
+ }
+ @Override
+ public Map<?, ?> getInfo() {
+ return ImmutableMap.of(
+ "srcAbsPath", mapper.getJcrPath(sourcePath),
+ "destAbsPath", getPath());
+ }
+ };
+ }
+
+ Event nodeReordered(
+ String parent, String name, String identifier,
+ final String destName) {
+ return new EventImpl(parent, name, identifier) {
+ @Override
+ public int getType() {
+ return NODE_MOVED;
+ }
+ @Override
+ public Map<?, ?> getInfo() {
+ return ImmutableMap.of(
+ "srcChildRelPath", mapper.getJcrName(name),
+ "destChildRelPath", mapper.getJcrName(destName));
+ }
+ };
+ }
+
+ //---------------------------------------------------------< EventImpl >--
+
+ private abstract class EventImpl implements JackrabbitEvent {
+
+ /**
+ * Path of the parent node of the item this event is about.
+ */
+ private final String parent;
+
+ /**
+ * Name of the item this event is about.
+ */
+ protected final String name;
+
+ private final String identifier;
+
+ EventImpl(String parent, String name, String identifier) {
+ this.parent = parent;
+ this.name = name;
+ this.identifier = identifier;
+ }
+
+ //---------------------------------------------------------< Event >--
+
+ @Override
+ public String getPath() {
+ return PathUtils.concat(
+ mapper.getJcrPath(parent), mapper.getJcrName(name));
+ }
+
+ @Override
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public Map<?, ?> getInfo() {
+ return emptyMap();
+ }
+
+ @Override
+ public String getUserID() {
+ return userID;
+ }
+
+ @Override
+ public String getUserData() {
+ return userData;
+ }
+
+ @Override
+ public long getDate() {
+ return date;
+ }
+
+ //-----------------------------------------------< JackrabbitEvent >--
+
+ @Override
+ public boolean isExternal() {
+ return external;
+ }
+
+ //--------------------------------------------------------< Object >--
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ } else if (object instanceof EventImpl) {
+ EventImpl that = (EventImpl) object;
+ return getType() == that.getType()
+ && getPath().equals(that.getPath())
+ && getIdentifier().equals(that.getIdentifier())
+ && getInfo().equals(that.getInfo())
+ && Objects.equal(getUserID(), that.getUserID())
+ && Objects.equal(getUserData(), that.getUserData())
+ && getDate() == that.getDate()
+ && isExternal() == that.isExternal();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(
+ getType(), getPath(), getIdentifier(), getInfo(),
+ getUserID(), getUserData(), getDate(), isExternal());
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper("Event")
+ .add("type", getType())
+ .add("path", getPath())
+ .add("identifier", getIdentifier())
+ .add("info", getInfo())
+ .add("userID", getUserID())
+ .add("userData", getUserData())
+ .add("date", getDate())
+ .add("external", isExternal())
+ .toString();
+ }
+
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventQueue.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventQueue.java?rev=1562562&r1=1562561&r2=1562562&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventQueue.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/EventQueue.java
Wed Jan 29 19:48:47 2014
@@ -53,8 +53,9 @@ class EventQueue implements EventIterato
@Nonnull NamePathMapper mapper, CommitInfo info,
@Nonnull NodeState before, @Nonnull NodeState after,
@Nonnull String basePath, @Nonnull EventFilter filter) {
- EventHandler handler = new QueueingHandler(
- this, mapper, info, before, after);
+ EventFactory factory = new EventFactory(mapper, info);
+ EventHandler handler =
+ new QueueingHandler(this, factory, before, after);
for (String name : PathUtils.elements(basePath)) {
before = before.getChildNode(name);
after = after.getChildNode(name);
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/QueueingHandler.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/QueueingHandler.java?rev=1562562&r1=1562561&r2=1562562&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/QueueingHandler.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/QueueingHandler.java
Wed Jan 29 19:48:47 2014
@@ -18,34 +18,18 @@
*/
package org.apache.jackrabbit.oak.jcr.observation;
-import static java.util.Collections.emptyMap;
-
-import java.util.Map;
-
-import org.apache.jackrabbit.api.observation.JackrabbitEvent;
import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.namepath.PathTracker;
import org.apache.jackrabbit.oak.plugins.identifier.IdentifierTracker;
import org.apache.jackrabbit.oak.plugins.observation.EventHandler;
-import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.state.NodeState;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableMap;
-
/**
* Change handler that generates JCR Event instances and places them
* in an event queue.
*/
class QueueingHandler implements EventHandler {
- /**
- * Dummy session identifier used to identify external commits.
- */
- private static final String OAK_EXTERNAL = "oak:external";
-
private final QueueingHandler parent;
private final String name;
@@ -56,16 +40,14 @@ class QueueingHandler implements EventHa
private final EventQueue queue;
- private final NamePathMapper mapper;
-
- private final CommitInfo info;
+ private final EventFactory factory;
private final NodeState before;
private final NodeState after;
QueueingHandler(
- EventQueue queue, NamePathMapper mapper, CommitInfo info,
+ EventQueue queue, EventFactory factory,
NodeState before, NodeState after) {
this.parent = null;
this.name = null;
@@ -78,14 +60,7 @@ class QueueingHandler implements EventHa
}
this.queue = queue;
- this.mapper = mapper;
- if (info != null) {
- this.info = info;
- } else {
- // Generate a dummy CommitInfo object to avoid extra null checks.
- // The current time is used as a rough estimate of the commit time.
- this.info = new CommitInfo(OAK_EXTERNAL, null, null);
- }
+ this.factory = factory;
this.before = before;
this.after = after;
@@ -107,8 +82,7 @@ class QueueingHandler implements EventHa
}
this.queue = parent.queue;
- this.mapper = parent.mapper;
- this.info = parent.info;
+ this.factory = parent.factory;
this.before = before;
this.after = after;
@@ -134,194 +108,55 @@ class QueueingHandler implements EventHa
@Override
public void propertyAdded(PropertyState after) {
- queue.addEvent(new ItemEvent(after.getName()) {
- @Override
- public int getType() {
- return PROPERTY_ADDED;
- }
- });
+ queue.addEvent(factory.propertyAdded(
+ pathTracker.getPath(), after.getName(),
+ identifierTracker.getIdentifier()));
}
@Override
public void propertyChanged(PropertyState before, PropertyState after) {
- queue.addEvent(new ItemEvent(after.getName()) {
- @Override
- public int getType() {
- return PROPERTY_CHANGED;
- }
- });
+ queue.addEvent(factory.propertyChanged(
+ pathTracker.getPath(), after.getName(),
+ identifierTracker.getIdentifier()));
}
@Override
public void propertyDeleted(PropertyState before) {
- queue.addEvent(new ItemEvent(before.getName()) {
- @Override
- public int getType() {
- return PROPERTY_REMOVED;
- }
- });
+ queue.addEvent(factory.propertyDeleted(
+ pathTracker.getPath(), before.getName(),
+ identifierTracker.getIdentifier()));
}
@Override
public void nodeAdded(String name, NodeState after) {
- queue.addEvent(new NodeEvent(name, after, identifierTracker) {
- @Override
- public int getType() {
- return NODE_ADDED;
- }
- });
+ queue.addEvent(factory.nodeAdded(
+ pathTracker.getPath(), name,
+ identifierTracker.getChildTracker(name,
after).getIdentifier()));
}
@Override
public void nodeDeleted(String name, NodeState before) {
- queue.addEvent(new NodeEvent(name, before,
getBeforeIdentifierTracker()) {
- @Override
- public int getType() {
- return NODE_REMOVED;
- }
- });
+ queue.addEvent(factory.nodeDeleted(
+ pathTracker.getPath(), name,
+ getBeforeIdentifierTracker().getChildTracker(name,
before).getIdentifier()));
}
@Override
public void nodeMoved(
final String sourcePath, String name, NodeState moved) {
- queue.addEvent(new NodeEvent(name, moved, identifierTracker) {
- @Override
- public int getType() {
- return NODE_MOVED;
- }
- @Override
- public Map<?, ?> getInfo() {
- return ImmutableMap.of(
- "srcAbsPath", mapper.getJcrPath(sourcePath),
- "destAbsPath", getPath());
- }
- });
+ queue.addEvent(factory.nodeMoved(
+ pathTracker.getPath(), name,
+ identifierTracker.getChildTracker(name, moved).getIdentifier(),
+ sourcePath));
}
@Override
public void nodeReordered(
final String destName, final String name, NodeState reordered) {
- queue.addEvent(new NodeEvent(name, reordered, identifierTracker) {
- @Override
- public int getType() {
- return NODE_MOVED;
- }
- @Override
- public Map<?, ?> getInfo() {
- return ImmutableMap.of(
- "srcChildRelPath", mapper.getJcrName(name),
- "destChildRelPath", mapper.getJcrName(destName));
- }
- });
- }
-
- //-----------------------------------------------------------< private >--
-
- private abstract class NodeEvent extends ItemEvent {
-
- private final String identifier;
-
- NodeEvent(String name, NodeState node, IdentifierTracker tracker) {
- super(name);
- this.identifier =
- tracker.getChildTracker(name, node).getIdentifier();
- }
-
- @Override
- public String getIdentifier() {
- return identifier;
- }
-
- }
-
- private abstract class ItemEvent implements JackrabbitEvent {
-
- protected final String name;
-
- ItemEvent(String name) {
- this.name = name;
- }
-
- @Override
- public String getPath() {
- return PathUtils.concat(
- mapper.getJcrPath(pathTracker.getPath()),
- mapper.getJcrName(name));
- }
-
- @Override
- public String getIdentifier() {
- return identifierTracker.getIdentifier();
- }
-
- @Override
- public Map<?, ?> getInfo() {
- return emptyMap();
- }
-
- @Override
- public String getUserID() {
- return info.getUserId();
- }
-
- @Override
- public String getUserData() {
- return info.getMessage();
- }
-
- @Override
- public long getDate() {
- return info.getDate();
- }
-
- @Override
- public boolean isExternal() {
- return info.getSessionId() == OAK_EXTERNAL;
- }
-
- //--------------------------------------------------------< Object >--
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- } else if (object instanceof ItemEvent) {
- ItemEvent that = (ItemEvent) object;
- return getType() == that.getType()
- && getPath().equals(that.getPath())
- && getIdentifier().equals(that.getIdentifier())
- && getInfo().equals(that.getInfo())
- && Objects.equal(getUserID(), that.getUserID())
- && Objects.equal(getUserData(), that.getUserData())
- && getDate() == that.getDate()
- && isExternal() == that.isExternal();
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(
- getType(), getPath(), getIdentifier(), getInfo(),
- getUserID(), getUserData(), getDate(), isExternal());
- }
-
- @Override
- public String toString() {
- return Objects.toStringHelper("Event")
- .add("type", getType())
- .add("path", getPath())
- .add("identifier", getIdentifier())
- .add("info", getInfo())
- .add("userID", getUserID())
- .add("userData", getUserData())
- .add("date", getDate())
- .add("external", isExternal())
- .toString();
- }
-
+ queue.addEvent(factory.nodeReordered(
+ pathTracker.getPath(), name,
+ identifierTracker.getChildTracker(name,
reordered).getIdentifier(),
+ destName));
}
}