[maven-release-plugin]  copy for tag jackrabbit-oak-1.3.9

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/tags/jackrabbit-oak-1.3.9@1710791 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateOp.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateOp.java
index ec6d967..d00d7e0 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateOp.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateOp.java
@@ -245,21 +245,59 @@
     }
 
     /**
+     * Checks if the property is equal to the given value.
+     *
+     * @param property the name of the property or map.
+     * @param value the value to compare to ({@code null} checks both for non-existence and the value being null)
+     */
+    void equals(@Nonnull String property, @Nullable Object value) {
+        equals(property, null, value);
+    }
+
+    /**
      * Checks if the property or map entry is equal to the given value.
      *
      * @param property the name of the property or map.
      * @param revision the revision within the map or {@code null} if this check
      *                 is for a property.
+     * @param value the value to compare to ({@code null} checks both for non-existence and the value being null)
+     */
+    void equals(@Nonnull String property,
+                @Nullable Revision revision,
+                @Nullable Object value) {
+        if (isNew) {
+            throw new IllegalStateException("Cannot perform equals check on new document");
+        }
+        getOrCreateConditions().put(new Key(property, revision),
+                Condition.newEqualsCondition(value));
+    }
+
+    /**
+     * Checks if the property does not exist or is not equal to the given value.
+     *
+     * @param property the name of the property or map.
+     * @param value the value to compare to.
+     */
+    void notEquals(@Nonnull String property, @Nullable Object value) {
+        notEquals(property, null, value);
+    }
+
+    /**
+     * Checks if the property or map entry does not exist or is not equal to the given value.
+     *
+     * @param property the name of the property or map.
+     * @param revision the revision within the map or {@code null} if this check
+     *                 is for a property.
      * @param value the value to compare to.
      */
-    void equals(@Nonnull String property,
-                @Nullable Revision revision,
-                @Nonnull Object value) {
+    void notEquals(@Nonnull String property,
+                   @Nullable Revision revision,
+                   @Nullable Object value) {
         if (isNew) {
-            throw new IllegalStateException("Cannot perform equals check on new document");
+            throw new IllegalStateException("Cannot perform notEquals check on new document");
         }
         getOrCreateConditions().put(new Key(property, revision),
-                Condition.newEqualsCondition(value));
+                Condition.newNotEqualsCondition(value));
     }
 
     /**
@@ -415,8 +453,12 @@
             /**
              * Checks if a map entry equals a given value.
              */
-            EQUALS
+            EQUALS,
 
+            /**
+             * Checks if a map entry does not equal a given value.
+             */
+            NOTEQUALS
         }
 
         /**
@@ -440,8 +482,18 @@
          * @param value the value to compare to.
          * @return the equals condition.
          */
-        public static Condition newEqualsCondition(@Nonnull Object value) {
-            return new Condition(Type.EQUALS, checkNotNull(value));
+        public static Condition newEqualsCondition(@Nullable Object value) {
+            return new Condition(Type.EQUALS, value);
+        }
+
+        /**
+         * Creates a new notEquals condition with the given value.
+         *
+         * @param value the value to compare to.
+         * @return the notEquals condition.
+         */
+        public static Condition newNotEqualsCondition(@Nullable Object value) {
+            return new Condition(Type.NOTEQUALS, value);
         }
 
         @Override
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtils.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtils.java
index 08a511b..0b41889 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtils.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtils.java
@@ -134,7 +134,7 @@
                         return false;
                     }
                 }
-            } else if (c.type == Condition.Type.EQUALS) {
+            } else if (c.type == Condition.Type.EQUALS || c.type == Condition.Type.NOTEQUALS) {
                 if (r != null) {
                     if (value instanceof Map) {
                         value = ((Map) value).get(r);
@@ -142,7 +142,10 @@
                         value = null;
                     }
                 }
-                if (!Objects.equal(value, c.value)) {
+                boolean equal = Objects.equal(value, c.value);
+                if (c.type == Condition.Type.EQUALS && !equal) {
+                    return false;
+                } else if (c.type == Condition.Type.NOTEQUALS && equal) {
                     return false;
                 }
             } else {
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
index 7beacd9..57a44ed 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
@@ -1272,6 +1272,9 @@
                 case EQUALS:
                     query.and(k.toString()).is(c.value);
                     break;
+                case NOTEQUALS:
+                    query.and(k.toString()).notEquals(c.value);
+                    break;
             }
         }
 
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Compactor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Compactor.java
index f9fb43f..7ac47dd 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Compactor.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Compactor.java
@@ -16,6 +16,18 @@
  */
 package org.apache.jackrabbit.oak.plugins.segment;
 
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.jackrabbit.oak.api.Type.BINARIES;
+import static org.apache.jackrabbit.oak.api.Type.BINARY;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
@@ -38,18 +50,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-import static org.apache.jackrabbit.oak.api.Type.BINARIES;
-import static org.apache.jackrabbit.oak.api.Type.BINARY;
-import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
-
 /**
  * Tool for compacting segments.
  */
@@ -115,7 +115,8 @@
     }
 
     public Compactor(FileStore store, CompactionStrategy compactionStrategy, Supplier<Boolean> cancel) {
-        this.writer = store.createSegmentWriter();
+        String wid = "c-" + store.getTracker().getCompactionMap().getGeneration() + 1;
+        this.writer = store.createSegmentWriter(wid);
         if (compactionStrategy.getPersistCompactionMap()) {
             this.map = new PersistedCompactionMap(store);
         } else {
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/PersistedCompactionMap.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/PersistedCompactionMap.java
index 51a065b..253b24d 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/PersistedCompactionMap.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/PersistedCompactionMap.java
@@ -193,7 +193,7 @@
             base = baseEntry == null ? null : new MapRecord(baseEntry.getValue());
 
             if (writer == null) {
-                writer = store.createSegmentWriter();
+                writer = store.createSegmentWriter(createWid());
             }
 
             Map<String, RecordId> offsetMap = newHashMap();
@@ -218,7 +218,7 @@
 
         if (!segmentIdMap.isEmpty()) {
             if (writer == null) {
-                writer = store.createSegmentWriter();
+                writer = store.createSegmentWriter(createWid());
             }
 
             RecordId previousBaseId = entries == null ? null : entries.getRecordId();
@@ -237,6 +237,11 @@
         }
     }
 
+    @Nonnull
+    private String createWid() {
+        return "cm-" + store.getTracker().getCompactionMap().getGeneration() + 1;
+    }
+
     /**
      * @return 0
      */
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
index 2382b88..a286977 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
@@ -24,6 +24,7 @@
 import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.CheckForNull;
@@ -106,6 +107,11 @@
      */
     private final CacheLIRS<SegmentId, Segment> segmentCache;
 
+    /**
+     * Number of segments
+     */
+    private final AtomicInteger segmentCounter = new AtomicInteger();
+
     public SegmentTracker(SegmentStore store, int cacheSizeMB,
             SegmentVersion version) {
         for (int i = 0; i < tables.length; i++) {
@@ -115,7 +121,7 @@
         this.store = store;
         this.compactionMap = new AtomicReference<CompactionMap>(
                 CompactionMap.EMPTY);
-        this.writer = new SegmentWriter(store, this, version);
+        this.writer = new SegmentWriter(store, this, version, "sys");
         StringCache c;
         if (DISABLE_STRING_CACHE) {
             c = null;
@@ -146,6 +152,14 @@
         this(store, DEFAULT_MEMORY_CACHE_SIZE, SegmentVersion.V_11);
     }
 
+    /**
+     * Increment and get the number of segments
+     * @return
+     */
+    int getNextSegmentNo() {
+        return segmentCounter.incrementAndGet();
+    }
+
     @Nonnull
     public CacheStats getSegmentCacheStats() {
         return new CacheStats(segmentCache, "Segment Cache", null, -1);
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
index 2ffa71b..c060239 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
@@ -28,6 +28,8 @@
 import static com.google.common.collect.Maps.newHashMap;
 import static com.google.common.collect.Maps.newLinkedHashMap;
 import static com.google.common.collect.Sets.newHashSet;
+import static java.lang.System.currentTimeMillis;
+import static java.lang.System.identityHashCode;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.nCopies;
@@ -54,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.jcr.PropertyType;
 
@@ -88,6 +91,8 @@
 
     static final int BLOCK_SIZE = 1 << 12; // 4kB
 
+    private static final AtomicInteger SEGMENT_COUNTER = new AtomicInteger();
+
     static byte[] createNewBuffer(SegmentVersion v) {
         byte[] buffer = new byte[Segment.MAX_SEGMENT_SIZE];
         buffer[0] = '0';
@@ -162,12 +167,54 @@
      */
     private final SegmentVersion version;
 
+    /**
+     * Id of this writer.
+     */
+    private final String wid;
+
     public SegmentWriter(SegmentStore store, SegmentTracker tracker, SegmentVersion version) {
+        this(store, tracker, version, null);
+    }
+
+    /**
+     * @param store     store to write to
+     * @param tracker   segment tracker for that {@code store}
+     * @param version   segment version to write
+     * @param wid       id of this writer
+     */
+    public SegmentWriter(SegmentStore store, SegmentTracker tracker, SegmentVersion version, String wid) {
         this.store = store;
         this.tracker = tracker;
         this.version = version;
         this.buffer = createNewBuffer(version);
+        this.wid = wid == null
+            ? "w-" + identityHashCode(this)
+            : wid;
+        newSegment(wid);
+    }
+
+    /**
+     * Allocate a new segment and write the segment meta data.
+     * The segment meta data is a string of the format {@code "{wid=W,sno=S,gc=G,t=T}"}
+     * where:
+     * <ul>
+     * <li>{@code W} is the writer id {@code wid}, </li>
+     * <li>{@code S} is a unique, increasing sequence number corresponding to the allocation order
+     * of the segments in this store, </li>
+     * <li>{@code G} is the garbage collection generation (i.e. the number of compaction cycles
+     * that have been run),</li>
+     * <li>{@code T} is a time stamp according to {@link System#currentTimeMillis()}.</li>
+     * </ul>
+     * The segment meta data is guaranteed to be the first string record in a segment.
+     * @param wid  the writer id
+     */
+    private void newSegment(String wid) {
         this.segment = new Segment(tracker, buffer);
+        writeString(
+            "{wid=" + wid +
+            ",sno=" + tracker.getNextSegmentNo() +
+            ",gc=" + tracker.getCompactionMap().getGeneration() +
+            ",t=" + currentTimeMillis() + "}");
     }
 
     /**
@@ -244,7 +291,7 @@
                 blobrefs.clear();
                 length = 0;
                 position = buffer.length;
-                segment = new Segment(tracker, buffer);
+                newSegment(wid);
             }
         }
 
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
index 238731a..42812ff 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
@@ -893,8 +893,8 @@
     /**
      * @return  a new {@link SegmentWriter} instance for writing to this store.
      */
-    public SegmentWriter createSegmentWriter() {
-        return new SegmentWriter(this, tracker, getVersion());
+    public SegmentWriter createSegmentWriter(String wid) {
+        return new SegmentWriter(this, tracker, getVersion(), wid);
     }
 
     /**
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/package-info.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/package-info.java
index ac5cfc4..cdaefb4 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/package-info.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("2.2.0")
+@Version("3.0.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.segment.file;
 
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/package-info.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/package-info.java
index 0ab6beb..a1b1a35 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/package-info.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("5.1.0")
+@Version("5.2.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.segment;
 
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java
index 9be5761..827bfe7 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java
@@ -68,6 +68,119 @@
     }
 
     @Test
+    public void testConditionalUpdate() {
+        String id = this.getClass().getName() + ".testConditionalUpdate";
+
+        // remove if present
+        NodeDocument nd = super.ds.find(Collection.NODES, id);
+        if (nd != null) {
+            super.ds.remove(Collection.NODES, id);
+        }
+
+        String existingProp = "_recoverylock";
+        String existingRevisionProp = "recoverylock";
+        String nonExistingProp = "_qux";
+        String nonExistingRevisionProp = "qux";
+        Revision r = new Revision(1, 1, 1);
+
+        // add
+        UpdateOp up = new UpdateOp(id, true);
+        up.set("_id", id);
+        up.set(existingProp, "lock");
+        up.setMapEntry(existingRevisionProp, r, "lock");
+        assertTrue(super.ds.create(Collection.NODES, Collections.singletonList(up)));
+
+        // updates
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.notEquals(nonExistingProp, "none");
+        NodeDocument result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(nonExistingProp, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.notEquals(nonExistingRevisionProp, r, "none");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(nonExistingRevisionProp, r, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingProp, "none");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingProp, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingRevisionProp, r, "none");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingRevisionProp, r, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.notEquals(existingProp, "lock");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingProp, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.notEquals(existingRevisionProp, r, "lock");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingRevisionProp, r, null);
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingProp, "lock");
+        up.set(existingProp, "none");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        up = new UpdateOp(id, false);
+        up.set("_id", id);
+        up.equals(existingRevisionProp, r, "lock");
+        up.setMapEntry(existingRevisionProp, r, "none");
+        result = super.ds.findAndUpdate(Collection.NODES, up);
+        assertNotNull(result);
+
+        removeMe.add(id);
+    }
+
+    @Test
     public void testMaxIdAscii() {
         int result = testMaxId(true);
         assertTrue("needs to support keys of 512 bytes length, but only supports " + result, result >= 512);
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateOpTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateOpTest.java
index 5c1b777..063f18b 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateOpTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateOpTest.java
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
-import com.google.common.collect.Lists;
-
 import org.junit.Test;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -44,15 +42,15 @@
         UpdateOp.Key k3 = new UpdateOp.Key("foo", null);
         assertTrue(k1.equals(k3));
         assertTrue(k3.equals(k1));
-        
+
         UpdateOp.Key k4 = new UpdateOp.Key("foo", r1);
         assertFalse(k4.equals(k3));
         assertFalse(k3.equals(k4));
-        
+
         UpdateOp.Key k5 = new UpdateOp.Key("foo", r2);
         assertFalse(k5.equals(k4));
         assertFalse(k4.equals(k5));
-        
+
         UpdateOp.Key k6 = new UpdateOp.Key("foo", r1);
         assertTrue(k6.equals(k4));
         assertTrue(k4.equals(k6));
@@ -144,6 +142,37 @@
     }
 
     @Test
+    public void notEqualsTest() {
+        Revision r = Revision.newRevision(1);
+        UpdateOp op = new UpdateOp("id", true);
+        try {
+            op.notEquals("p", r, "v");
+            fail("expected " + IllegalStateException.class.getName());
+        } catch (IllegalStateException e) {
+            // expected
+        }
+        op = new UpdateOp("id", false);
+        op.notEquals("p", r, "v");
+        assertEquals(1, op.getConditions().size());
+        UpdateOp.Key key = op.getConditions().keySet().iterator().next();
+        assertEquals(r, key.getRevision());
+        assertEquals("p", key.getName());
+        UpdateOp.Condition c = op.getConditions().get(key);
+        assertEquals(UpdateOp.Condition.Type.NOTEQUALS, c.type);
+        assertEquals("v", c.value);
+
+        op = new UpdateOp("id", false);
+        op.notEquals("p", r, null);
+        assertEquals(1, op.getConditions().size());
+        key = op.getConditions().keySet().iterator().next();
+        assertEquals(r, key.getRevision());
+        assertEquals("p", key.getName());
+        c = op.getConditions().get(key);
+        assertEquals(UpdateOp.Condition.Type.NOTEQUALS, c.type);
+        assertEquals(null, c.value);
+    }
+
+    @Test
     public void getChanges() {
         UpdateOp op = new UpdateOp("id", false);
         assertEquals(0, op.getChanges().size());
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtilsTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtilsTest.java
index c85d179..8653f29 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtilsTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/UpdateUtilsTest.java
@@ -111,32 +111,84 @@
         assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
+        op.notEquals("t", r, "value");
+        assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
         op.equals("t", r, "foo");
         assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
+        op.notEquals("t", r, "foo");
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
         op.equals("t", Revision.newRevision(1), "value");
         assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
-        op.equals("t", null, "value");
+        op.notEquals("t", Revision.newRevision(1), "value");
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.equals("t", "value");
         assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
+        op.notEquals("t", "value");
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
         op.equals("p", r, 42L);
         assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
-        op.equals("p", null, 42L);
+        op.notEquals("p", r, 42L);
         assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
 
         op = newUpdateOp(id);
-        op.equals("p", null, 7L);
+        op.equals("p", 42L);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.notEquals("p", 42L);
         assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
+        op = newUpdateOp(id);
+        op.equals("p", 7L);
+        assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
 
+        op = newUpdateOp(id);
+        op.notEquals("p", 7L);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        // check on non-existing property
+        op = newUpdateOp(id);
+        op.notEquals("other", 7L);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.notEquals("other", r, 7L);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.notEquals("other", r, null);
+        assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.equals("other", r, null);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        // check null
+        op = newUpdateOp(id);
+        op.notEquals("p", null);
+        assertTrue(UpdateUtils.checkConditions(d, op.getConditions()));
+
+        op = newUpdateOp(id);
+        op.notEquals("other", r, null);
+        assertFalse(UpdateUtils.checkConditions(d, op.getConditions()));
     }
-    
+
     private static UpdateOp newUpdateOp(String id) {
         return new UpdateOp(id, false);
     }
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java
index 6ab0dee..d8bd7f7 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java
@@ -40,29 +40,29 @@
     @Test
     public void testNodeSize() {
         NodeBuilder builder = EMPTY_NODE.builder();
-        assertEquals(48, getSize(builder));
+        assertEquals(96, getSize(builder));
         assertEquals(4, getAmortizedSize(builder));
 
         builder = EMPTY_NODE.builder();
         builder.setProperty("foo", "bar");
-        assertEquals(48, getSize(builder));
+        assertEquals(96, getSize(builder));
         assertEquals(8, getAmortizedSize(builder));
 
         builder = EMPTY_NODE.builder();
         builder.setProperty("foo", "bar");
         builder.setProperty("baz", 123);
-        assertEquals(80, getSize(builder));
+        assertEquals(128, getSize(builder));
         assertEquals(16, getAmortizedSize(builder));
 
         builder = EMPTY_NODE.builder();
         builder.child("foo");
-        assertEquals(64, getSize(builder));
+        assertEquals(112, getSize(builder));
         assertEquals(12, getAmortizedSize(builder));
 
         builder = EMPTY_NODE.builder();
         builder.child("foo");
         builder.child("bar");
-        assertEquals(96, getSize(builder));
+        assertEquals(144, getSize(builder));
         assertEquals(40, getAmortizedSize(builder));
     }
 
@@ -118,7 +118,7 @@
     public void testAccessControlNodes() {
         NodeBuilder builder = EMPTY_NODE.builder();
         builder.setProperty("jcr:primaryType", "rep:ACL", Type.NAME);
-        assertEquals(48, getSize(builder));
+        assertEquals(96, getSize(builder));
         assertEquals(4, getAmortizedSize(builder));
 
         NodeBuilder deny = builder.child("deny");
@@ -126,7 +126,7 @@
         deny.setProperty("rep:principalName", "everyone");
         deny.setProperty(PropertyStates.createProperty(
                 "rep:privileges", ImmutableList.of("jcr:read"), Type.NAMES));
-        assertEquals(176, getSize(builder));
+        assertEquals(224, getSize(builder));
         assertEquals(32, getAmortizedSize(builder));
 
         NodeBuilder allow = builder.child("allow");
@@ -134,7 +134,7 @@
         allow.setProperty("rep:principalName", "administrators");
         allow.setProperty(PropertyStates.createProperty(
                 "rep:privileges", ImmutableList.of("jcr:all"), Type.NAMES));
-        assertEquals(320, getSize(builder));
+        assertEquals(352, getSize(builder));
         assertEquals(84, getAmortizedSize(builder));
 
         NodeBuilder deny0 = builder.child("deny0");
@@ -143,7 +143,7 @@
         deny0.setProperty("rep:glob", "*/activities/*");
         builder.setProperty(PropertyStates.createProperty(
                 "rep:privileges", ImmutableList.of("jcr:read"), Type.NAMES));
-        assertEquals(416, getSize(builder));
+        assertEquals(464, getSize(builder));
         assertEquals(124, getAmortizedSize(builder));
 
         NodeBuilder allow0 = builder.child("allow0");
@@ -151,7 +151,7 @@
         allow0.setProperty("rep:principalName", "user-administrators");
         allow0.setProperty(PropertyStates.createProperty(
                 "rep:privileges", ImmutableList.of("jcr:all"), Type.NAMES));
-        assertEquals(480, getSize(builder));
+        assertEquals(528, getSize(builder));
         assertEquals(160, getAmortizedSize(builder));
     }
 
@@ -168,7 +168,7 @@
         SegmentNodeState state = writer.writeNode(builder.getNodeState());
         writer.flush();
         Segment segment = store.readSegment(state.getRecordId().getSegmentId());
-        assertEquals(27520, segment.size());
+        assertEquals(27568, segment.size());
 
         writer.flush(); // force flushing of the previous segment
 
@@ -177,7 +177,7 @@
         state = writer.writeNode(builder.getNodeState());
         writer.flush();
         segment = store.readSegment(state.getRecordId().getSegmentId());
-        assertEquals(496, segment.size());
+        assertEquals(544, segment.size());
     }
 
     private int getSize(NodeBuilder builder) {