You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2012/02/13 10:58:26 UTC

svn commit: r1243459 - in /jackrabbit/sandbox/microkernel/src: main/java/org/apache/jackrabbit/mk/util/Sync.java test/java/org/apache/jackrabbit/mk/util/SyncTest.java

Author: thomasm
Date: Mon Feb 13 09:58:25 2012
New Revision: 1243459

URL: http://svn.apache.org/viewvc?rev=1243459&view=rev
Log:
Sync utility.

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/Sync.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/SyncTest.java

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/Sync.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/Sync.java?rev=1243459&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/Sync.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/Sync.java Mon Feb 13 09:58:25 2012
@@ -0,0 +1,160 @@
+/*
+ * 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.mk.util;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.mem.NodeImpl;
+
+/**
+ * Traverse the nodes in two repositories / revisions / nodes in order to
+ * synchronize them or list the differences.
+ * <p>
+ * If the target is not set, the tool can be used to list or backup the content,
+ * for (data store) garbage collection, or similar.
+ */
+public class Sync {
+
+    private MicroKernel sourceMk, targetMk;
+    private String sourceRev, targetRev;
+    private String sourcePath, targetPath = "/";
+
+    private Handler handler;
+
+    /**
+     * Set the source (required).
+     *
+     * @param mk the source
+     * @param rev the revision
+     * @param path the path
+     */
+    public void setSource(MicroKernel mk, String rev, String path) {
+        sourceMk = mk;
+        sourceRev = rev;
+        sourcePath = path;
+    }
+
+    /**
+     * Set the target (optional). If not set, the tool assumes no nodes exist on
+     * the target.
+     *
+     * @param mk the target
+     * @param rev the revision
+     * @param path the path
+     */
+
+    public void setTarget(MicroKernel mk, String rev, String path) {
+        targetMk = mk;
+        targetRev = rev;
+        targetPath = path;
+    }
+
+    public void run(Handler handler) {
+        this.handler = handler;
+        visit("");
+    }
+
+    public void visit(String relPath) {
+        String source = PathUtils.concat(sourcePath, relPath);
+        String target = PathUtils.concat(targetPath, relPath);
+        NodeImpl s = null, t = null;
+        // TODO support large child node lists
+        if (sourceMk.nodeExists(source, sourceRev)) {
+            s = NodeImpl.parse(sourceMk.getNodes(source, sourceRev, 0, 0, Integer.MAX_VALUE));
+        }
+        if (targetMk != null && targetMk.nodeExists(target, targetRev)) {
+            t = NodeImpl.parse(targetMk.getNodes(target, targetRev, 0, 0, Integer.MAX_VALUE));
+        }
+        if (s == null || t == null) {
+            if (s == t) {
+                // both don't exist - ok
+                return;
+            } else if (s == null) {
+                handler.removeNode(target);
+                return;
+            } else {
+                if (!PathUtils.denotesRoot(target)) {
+                    handler.addNode(target);
+                }
+            }
+        }
+        // properties
+        for (int i = 0; i < s.getPropertyCount(); i++) {
+            String name = s.getProperty(i);
+            String sourceValue = s.getPropertyValue(i);
+            String targetValue = t != null && t.hasProperty(name) ? t.getProperty(name) : null;
+            if (!sourceValue.equals(targetValue)) {
+                handler.setProperty(target, name, sourceValue);
+            }
+        }
+        for (int i = 0; t != null && i < t.getPropertyCount(); i++) {
+            String name = t.getProperty(i);
+            // if it exists in the source, it's already updated
+            if (!s.hasProperty(name)) {
+                handler.setProperty(target, name, null);
+            }
+        }
+        // child nodes
+        for (int i = 0;; i++) {
+            String name = s.getChildNodeName(i);
+            if (name == null) {
+                break;
+            }
+            visit(PathUtils.concat(relPath, name));
+        }
+        for (int i = 0; t != null; i++) {
+            String name = t.getChildNodeName(i);
+            if (name == null) {
+                break;
+            }
+            // if it exists in the source, it's already updated
+            if (!s.exists(name)) {
+                visit(PathUtils.concat(relPath, name));
+            }
+        }
+    }
+
+    /**
+     * The sync handler.
+     */
+    public interface Handler {
+
+        /**
+         * The given node needs to be added to the target.
+         *
+         * @param path the path
+         */
+        void addNode(String path);
+
+        /**
+         * The given node needs to be removed from the target.
+         *
+         * @param path the path
+         */
+        void removeNode(String target);
+
+        /**
+         * The given property needs to be set on the target.
+         *
+         * @param path the path
+         * @param property the property name
+         * @param value the new value, or null to remove it
+         */
+        void setProperty(String target, String property, String value);
+
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/SyncTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/SyncTest.java?rev=1243459&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/SyncTest.java (added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/SyncTest.java Mon Feb 13 09:58:25 2012
@@ -0,0 +1,103 @@
+/*
+ * 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.mk.util;
+
+import static org.junit.Assert.assertEquals;
+import org.apache.jackrabbit.mk.MultiMkTestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test the sync util.
+ */
+@RunWith(Parameterized.class)
+public class SyncTest extends MultiMkTestBase {
+
+    public SyncTest(String url) {
+        super(url);
+    }
+
+    @Test
+    public void test() {
+        mk.commit("/", "+ \"source\": { \"id\": 1, \"plus\": 0, \"a\": { \"x\": 10, \"y\": 20 }, \"b\": {\"z\": 100}, \"d\":{} }", mk.getHeadRevision(), "");
+        Sync sync = new Sync();
+        String head = mk.getHeadRevision();
+        sync.setSource(mk, head, "/");
+        String diff = syncToString(sync);
+        assertEquals(
+                "add /source\n" +
+                "setProperty /source id=1\n" +
+                "setProperty /source plus=0\n" +
+                "add /source/a\n" +
+                "setProperty /source/a x=10\n" +
+                "setProperty /source/a y=20\n" +
+                "add /source/b\n" +
+                "setProperty /source/b z=100\n" +
+                "add /source/d\n",
+                diff);
+
+        mk.commit("/", "+ \"target\": { \"id\": 2, \"minus\": 0, \"a\": { \"x\": 10 }, \"c\": {} }", mk.getHeadRevision(), "");
+        head = mk.getHeadRevision();
+        sync.setSource(mk, head, "/source");
+        sync.setTarget(mk, head, "/target");
+        diff = syncToString(sync);
+        assertEquals(
+                "setProperty /target id=1\n" +
+                "setProperty /target plus=0\n" +
+                "setProperty /target minus=null\n" +
+                "setProperty /target/a y=20\n" +
+                "add /target/b\n" +
+                "setProperty /target/b z=100\n" +
+                "add /target/d\n" +
+                "remove /target/c\n", diff);
+
+        sync.setSource(mk, head, "/notExist");
+        sync.setTarget(mk, head, "/target");
+        diff = syncToString(sync);
+        assertEquals(
+                "remove /target\n", diff);
+
+        sync.setSource(mk, head, "/notExist");
+        sync.setTarget(mk, head, "/notExist2");
+        diff = syncToString(sync);
+        assertEquals("", diff);
+
+    }
+
+    private static String syncToString(Sync sync) {
+        final StringBuilder buff = new StringBuilder();
+        sync.run(new Sync.Handler() {
+
+            @Override
+            public void addNode(String targetPath) {
+                buff.append("add ").append(targetPath).append('\n');
+            }
+
+            @Override
+            public void removeNode(String targetPath) {
+                buff.append("remove ").append(targetPath).append('\n');
+            }
+
+            @Override
+            public void setProperty(String targetPath, String property, String value) {
+                buff.append("setProperty ").append(targetPath).append(' ').
+                        append(property).append('=').append(value).append('\n');
+            }
+        });
+        return buff.toString();
+
+    }
+
+}