Index: src/main/java/org/apache/jackrabbit/core/cluster/ChangeLogRecord.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ChangeLogRecord.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ChangeLogRecord.java (working copy)
@@ -43,6 +43,11 @@
public class ChangeLogRecord extends ClusterRecord {
/**
+ * Identifier: DATE
+ */
+ static final char DATE_IDENTIFIER = 'D';
+
+ /**
* Identifier: NODE.
*/
static final char NODE_IDENTIFIER = 'N';
@@ -78,6 +83,11 @@
private ChangeLog changes;
/**
+ * The time when the changes happened. Milliseconds since January 1 1970 UTC.
+ */
+ private long timestamp = System.currentTimeMillis();
+
+ /**
* List of EventStates.
*/
private List events;
@@ -96,7 +106,7 @@
* Create a new instance of this class. Used when serializing.
*
* @param changes changes
- * @param list of EventStates
+ * @param events list of EventStates
* @param record record
* @param workspace workspace
*/
@@ -131,6 +141,9 @@
while (identifier != END_MARKER) {
switch (identifier) {
+ case DATE_IDENTIFIER:
+ readTimestampRecord();
+ break;
case NODE_IDENTIFIER:
readNodeRecord();
break;
@@ -158,6 +171,15 @@
}
/**
+ * Reads the timestamp record.
+ *
+ * @throws JournalException if an error occurs.
+ */
+ private void readTimestampRecord() throws JournalException {
+ timestamp = record.readLong();
+ }
+
+ /**
* Read a node record.
*
* @throws JournalException if an error occurs
@@ -288,6 +310,7 @@
* {@inheritDoc}
*/
protected void doWrite() throws JournalException {
+ writeTimestampRecord();
Iterator deletedStates = changes.deletedStates();
while (deletedStates.hasNext()) {
ItemState state = (ItemState) deletedStates.next();
@@ -324,6 +347,16 @@
}
/**
+ * Writes the timestamp record.
+ *
+ * @throws JournalException if an error occurs.
+ */
+ private void writeTimestampRecord() throws JournalException {
+ record.writeChar(DATE_IDENTIFIER);
+ record.writeLong(timestamp);
+ }
+
+ /**
* Write a node record
*
* @param operation operation
@@ -397,9 +430,17 @@
* Return the events.
*
* @return events
- * @return
*/
public List getEvents() {
return Collections.unmodifiableList(events);
}
+
+ /**
+ * Returns the timestamp.
+ *
+ * @return the timestamp.
+ */
+ public long getTimestamp() {
+ return timestamp;
+ }
}
Index: src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java (working copy)
@@ -849,7 +849,8 @@
}
}
try {
- listener.externalUpdate(record.getChanges(), record.getEvents());
+ listener.externalUpdate(record.getChanges(),
+ record.getEvents(), record.getTimestamp());
} catch (RepositoryException e) {
String msg = "Unable to deliver update events: " + e.getMessage();
log.error(msg);
Index: src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java (working copy)
@@ -40,6 +40,7 @@
case ChangeLogRecord.NODE_IDENTIFIER:
case ChangeLogRecord.PROPERTY_IDENTIFIER:
case ChangeLogRecord.EVENT_IDENTIFIER:
+ case ChangeLogRecord.DATE_IDENTIFIER:
clusterRecord = new ChangeLogRecord(c, record, workspace);
clusterRecord.read();
break;
Index: src/main/java/org/apache/jackrabbit/core/cluster/Update.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/Update.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/cluster/Update.java (working copy)
@@ -57,4 +57,11 @@
*/
List getEvents();
+ /**
+ * Returns the timestamp whe this update occured.
+ *
+ * @return the timestamp whe this update occured.
+ */
+ long getTimestamp();
+
}
Index: src/main/java/org/apache/jackrabbit/core/cluster/UpdateEventListener.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/UpdateEventListener.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/cluster/UpdateEventListener.java (working copy)
@@ -30,8 +30,10 @@
*
* @param changes external changes containing only node and property ids.
* @param events events to deliver
+ * @param timestamp when the change occured.
* @throws RepositoryException if the update cannot be processed
*/
- void externalUpdate(ChangeLog changes, List events) throws RepositoryException;
+ void externalUpdate(ChangeLog changes, List events, long timestamp)
+ throws RepositoryException;
}
Index: src/main/java/org/apache/jackrabbit/core/journal/AppendRecord.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/journal/AppendRecord.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/journal/AppendRecord.java (working copy)
@@ -205,6 +205,20 @@
/**
* {@inheritDoc}
*/
+ public void writeLong(long n) throws JournalException {
+ checkOutput();
+
+ try {
+ dataOut.writeLong(n);
+ } catch (IOException e) {
+ String msg = "I/O error while writing long.";
+ throw new JournalException(msg, e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void writeString(String s) throws JournalException {
checkOutput();
@@ -386,6 +400,10 @@
throw unsupported();
}
+ public long readLong() throws JournalException {
+ throw unsupported();
+ }
+
public String readString() throws JournalException {
throw unsupported();
}
Index: src/main/java/org/apache/jackrabbit/core/journal/ReadRecord.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/journal/ReadRecord.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/journal/ReadRecord.java (working copy)
@@ -154,6 +154,20 @@
/**
* {@inheritDoc}
*/
+ public long readLong() throws JournalException {
+ consumed = true;
+
+ try {
+ return dataIn.readLong();
+ } catch (IOException e) {
+ String msg = "I/O error while reading long.";
+ throw new JournalException(msg, e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public String readString() throws JournalException {
consumed = true;
@@ -241,6 +255,10 @@
throw unsupported();
}
+ public void writeLong(long n) throws JournalException {
+ throw unsupported();
+ }
+
public void writeString(String s) throws JournalException {
throw unsupported();
}
Index: src/main/java/org/apache/jackrabbit/core/journal/Record.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/journal/Record.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/journal/Record.java (working copy)
@@ -81,6 +81,14 @@
int readInt() throws JournalException;
/**
+ * Read a long from the underlying stream.
+ *
+ * @return long value.
+ * @throws JournalException if an error occurs
+ */
+ long readLong() throws JournalException;
+
+ /**
* Read a string from the underlying stream.
*
* @return string or null
@@ -177,6 +185,14 @@
void writeInt(int n) throws JournalException;
/**
+ * Write a long to the underlying stream.
+ *
+ * @param n long
+ * @throws JournalException if an error occurs
+ */
+ void writeLong(long n) throws JournalException;
+
+ /**
* Write a string to the underlying stream.
*
* @param s string, may be null
Index: src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java (working copy)
@@ -26,7 +26,6 @@
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
-import javax.jcr.observation.Event;
/**
* Implementation of the {@link javax.jcr.observation.Event} and
@@ -46,14 +45,14 @@
private final SessionImpl session;
/**
- * The ItemManager of the session.
+ * The shared {@link EventState} object.
*/
- //private final ItemManager itemMgr;
+ private final EventState eventState;
/**
- * The shared {@link EventState} object.
+ * The timestamp of this event.
*/
- private final EventState eventState;
+ private final long timestamp;
/**
* Cached String value of this Event instance.
@@ -67,10 +66,12 @@
* @param session the session of the registerd EventListener
* where this Event will be delivered to.
* @param eventState the underlying EventState.
+ * @param timestamp the time when the change occured that caused this event.
*/
- EventImpl(SessionImpl session, EventState eventState) {
+ EventImpl(SessionImpl session, EventState eventState, long timestamp) {
this.session = session;
this.eventState = eventState;
+ this.timestamp = timestamp;
}
//---------------------------------------------------------------< Event >
@@ -96,6 +97,13 @@
return eventState.getUserId();
}
+ /**
+ * {@inheritDoc}
+ */
+ public long getDate() {
+ return timestamp;
+ }
+
//-----------------------------------------------------------< EventImpl >
/**
Index: src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (working copy)
@@ -88,6 +88,11 @@
private final Path pathPrefix;
/**
+ * Timestamp when this collection was created.
+ */
+ private long timestamp = System.currentTimeMillis();
+
+ /**
* Creates a new empty EventStateCollection.
*
FilteredEventIterator.
*
* @param c an unmodifiable Collection of {@link javax.jcr.observation.Event}s.
@@ -78,6 +83,7 @@
actualEvents = c.iterator();
this.filter = filter;
this.denied = denied;
+ this.timestamp = c.getTimestamp();
fetchNext();
}
@@ -158,7 +164,8 @@
// check denied set
if (denied == null || !denied.contains(state.getTargetId())) {
try {
- next = filter.blocks(state) ? null : new EventImpl(filter.getSession(), state);
+ next = filter.blocks(state) ? null : new EventImpl(
+ filter.getSession(), state, timestamp);
} catch (RepositoryException e) {
log.error("Exception while applying filter.", e);
}
Index: src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (working copy)
@@ -2096,11 +2096,14 @@
/**
* {@inheritDoc}
*/
- public void externalUpdate(ChangeLog external, List events) throws RepositoryException {
+ public void externalUpdate(ChangeLog external,
+ List events,
+ long timestamp) throws RepositoryException {
try {
EventStateCollection esc = new EventStateCollection(
getObservationDispatcher(), null, null);
esc.addAll(events);
+ esc.setTimestamp(timestamp);
getItemStateProvider().externalUpdate(external, esc);
} catch (IllegalStateException e) {
Index: src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (working copy)
@@ -515,6 +515,11 @@
private HashMap attributes;
/**
+ * Timestamp when this update was created.
+ */
+ private long timestamp = System.currentTimeMillis();
+
+ /**
* Create a new instance of this class.
*/
public Update(ChangeLog local, EventStateCollectionFactory factory,
@@ -835,6 +840,13 @@
}
/**
+ * {@inheritDoc}
+ */
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
* Updates the target node references collections based on the
* modifications in the change log (i.e. added/removed/modified
* REFERENCE properties).
@@ -843,9 +855,6 @@
* only be called once per change log and the change log
* should not be modified anymore afterwards.
*
- * @param changes change log
- * @param virtualProvider virtual provider that may already contain a
- * node references object
* @throws ItemStateException if an error occurs
*/
private void updateReferences() throws ItemStateException {
Index: src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (revision 713072)
+++ src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (working copy)
@@ -494,9 +494,11 @@
/**
* {@inheritDoc}
*/
- public void externalUpdate(ChangeLog changes, List events) throws RepositoryException {
+ public void externalUpdate(ChangeLog changes, List events, long timestamp)
+ throws RepositoryException {
EventStateCollection esc = getEscFactory().createEventStateCollection(null);
esc.addAll(events);
+ esc.setTimestamp(timestamp);
sharedStateMgr.externalUpdate(changes, esc);
}
Index: src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java (revision 713072)
+++ src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java (working copy)
@@ -28,7 +28,6 @@
import javax.jcr.nodetype.NoSuchNodeTypeException;
import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.cluster.LockEventListener;
import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
import org.apache.jackrabbit.core.state.ChangeLog;
@@ -413,10 +412,10 @@
/**
* {@inheritDoc}
*/
- public void externalUpdate(ChangeLog changes, List events)
+ public void externalUpdate(ChangeLog changes, List events, long timestamp)
throws RepositoryException {
- clusterEvents.add(new UpdateEvent(changes, events));
+ clusterEvents.add(new UpdateEvent(changes, events, timestamp));
}
@@ -441,14 +440,21 @@
private final transient Map attributes = new HashMap();
/**
+ * Timestamp when the changes in this update event occured.
+ */
+ private final long timestamp;
+
+ /**
* Create a new instance of this class.
*
* @param changes change log
* @param events list of EventStates
+ * @param timestamp time when the changes in this event occured.
*/
- public UpdateEvent(ChangeLog changes, List events) {
+ public UpdateEvent(ChangeLog changes, List events, long timestamp) {
this.changes = changes;
this.events = events;
+ this.timestamp = timestamp;
}
/**
@@ -472,6 +478,13 @@
/**
* {@inheritDoc}
*/
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
@@ -487,7 +500,7 @@
* {@inheritDoc}
*/
public int hashCode() {
- return changes.hashCode() ^ events.hashCode();
+ return changes.hashCode() ^ events.hashCode() ^ (int) (timestamp ^ (timestamp >>> 32));
}
/**
@@ -497,7 +510,8 @@
if (obj instanceof UpdateEvent) {
UpdateEvent other = (UpdateEvent) obj;
return SimpleEventListener.equals(changes, other.changes) &&
- SimpleEventListener.equals(events, other.events);
+ SimpleEventListener.equals(events, other.events) &&
+ timestamp == other.timestamp;
}
return false;
}
Index: src/test/java/org/apache/jackrabbit/core/cluster/UpdateEventFactory.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/cluster/UpdateEventFactory.java (revision 713072)
+++ src/test/java/org/apache/jackrabbit/core/cluster/UpdateEventFactory.java (working copy)
@@ -116,7 +116,7 @@
events.add(createEventState(p2, n2, Event.PROPERTY_REMOVED));
events.add(createEventState(n3, Event.NODE_REMOVED, "{}n3"));
- return new UpdateEvent(changes, events);
+ return new UpdateEvent(changes, events, System.currentTimeMillis());
}
@@ -139,6 +139,7 @@
*
* @param parentId parent node id
* @param name property name
+ * @return property state.
*/
protected PropertyState createPropertyState(NodeId parentId, String name) {
Name propName = nameFactory.create(name);
@@ -176,9 +177,9 @@
/**
* Create an event state for a property operation.
*
- * @param n node state
+ * @param p property state
+ * @param parent parent node state
* @param type Event.NODE_ADDED or Event.NODE_REMOVED
- * @param name property name
* @return event state
*/
protected EventState createEventState(PropertyState p, NodeState parent, int type) {