You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2008/10/07 14:43:57 UTC

svn commit: r702459 [2/2] - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/cluster/ main/java/org/apache/jackrabbit/core/journal/ main/java/org/apache/jackrabbit/core/state/ test/java/org/apache/jackrabbit/core/cluster/ ...

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java?rev=702459&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java Tue Oct  7 05:43:55 2008
@@ -0,0 +1,412 @@
+/*
+ * 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.core.cluster;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.cluster.SimpleEventListener.LockEvent;
+import org.apache.jackrabbit.core.cluster.SimpleEventListener.NamespaceEvent;
+import org.apache.jackrabbit.core.cluster.SimpleEventListener.NodeTypeEvent;
+import org.apache.jackrabbit.core.cluster.SimpleEventListener.UnlockEvent;
+import org.apache.jackrabbit.core.cluster.SimpleEventListener.UpdateEvent;
+import org.apache.jackrabbit.core.config.BeanConfig;
+import org.apache.jackrabbit.core.config.ClusterConfig;
+import org.apache.jackrabbit.core.config.JournalConfig;
+import org.apache.jackrabbit.core.journal.MemoryJournal;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
+import org.apache.jackrabbit.core.observation.EventState;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.test.JUnitTest;
+import org.apache.jackrabbit.uuid.UUID;
+
+/**
+ * Test cases for cluster record production and consumption. Verifies that
+ * cluster record serialization and deseralization is correct.
+ */
+public class ClusterRecordTest extends JUnitTest {
+
+    /**
+     * Defaut workspace name.
+     */
+    private static final String DEFAULT_WORKSPACE = "default";
+
+    /**
+     * Default user.
+     */
+    private static final String DEFAULT_USER = "admin";
+
+    /**
+     * Root node id.
+     */
+    private static final NodeId ROOT_NODE_ID = RepositoryImpl.ROOT_NODE_ID;
+
+    /**
+     * Default sync delay: 5 seconds.
+     */
+    private static final long SYNC_DELAY = 5000;
+
+    /**
+     * Default session, used for event state creation.
+     */
+    private final Session session = new ClusterSession(DEFAULT_USER);
+
+    /**
+     * Name factory.
+     */
+    private NameFactory nameFactory = NameFactoryImpl.getInstance();
+
+    /**
+     * Path factory.
+     */
+    private PathFactory pathFactory = PathFactoryImpl.getInstance();
+
+    /**
+     * Records shared among multiple memory journals.
+     */
+    private ArrayList records = new ArrayList();
+
+    /**
+     * Master.
+     */
+    private ClusterNode master;
+
+    /**
+     * Slave.
+     */
+    private ClusterNode slave;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void setUp() throws Exception {
+        master = createClusterNode("master", records);
+        master.start();
+
+        slave = createClusterNode("slave", records);
+
+        super.setUp();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void tearDown() throws Exception {
+        if (master != null) {
+            master.stop();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Test producing and consuming an update.
+     * @throws Exception
+     */
+    public void testUpdateOperation() throws Exception {
+        NodeState n1 = createNodeState();
+        NodeState n2 = createNodeState();
+        NodeState n3 = createNodeState();
+        PropertyState p1 = createPropertyState(n1.getNodeId(), "{}a");
+        PropertyState p2 = createPropertyState(n2.getNodeId(), "{}b");
+
+        ChangeLog changes = new ChangeLog();
+        changes.added(n1);
+        changes.added(p1);
+        changes.deleted(p2);
+        changes.modified(n2);
+        changes.deleted(n3);
+
+        List events = new ArrayList();
+        events.add(createEventState(n1, Event.NODE_ADDED, "{}n1"));
+        events.add(createEventState(p1, n1, Event.PROPERTY_ADDED));
+        events.add(createEventState(p2, n2, Event.PROPERTY_REMOVED));
+        events.add(createEventState(n3, Event.NODE_REMOVED, "{}n3"));
+
+        UpdateEvent update = new UpdateEvent(changes, events);
+
+        UpdateEventChannel channel = master.createUpdateChannel(DEFAULT_WORKSPACE);
+        channel.updateCreated(update);
+        channel.updatePrepared(update);
+        channel.updateCommitted(update);
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.createUpdateChannel(DEFAULT_WORKSPACE).setListener(listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), update);
+    }
+
+    /**
+     * Test producing and consuming a lock operation.
+     * @throws Exception
+     */
+    public void testLockOperation() throws Exception {
+        LockEvent event = new LockEvent(new NodeId(UUID.randomUUID()), true, "admin");
+
+        master.createLockChannel(DEFAULT_WORKSPACE).create(event.getNodeId(),
+                event.isDeep(), event.getUserId()).ended(true);
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.createLockChannel(DEFAULT_WORKSPACE).setListener(listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming an unlock operation.
+     * @throws Exception
+     */
+    public void testUnlockOperation() throws Exception {
+        UnlockEvent event = new UnlockEvent(new NodeId(UUID.randomUUID()));
+
+        master.createLockChannel(DEFAULT_WORKSPACE).create(event.getNodeId()).ended(true);
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.createLockChannel(DEFAULT_WORKSPACE).setListener(listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming a node type registration.
+     * @throws Exception
+     */
+    public void testNodeTypeRegistration() throws Exception {
+        NodeTypeDef ntd = new NodeTypeDef();
+        ntd.setName(NameFactoryImpl.getInstance().create("", "test"));
+        ntd.setSupertypes(new Name[]{NameConstants.NT_BASE});
+
+        ArrayList list = new ArrayList();
+        list.add(ntd);
+
+        NodeTypeEvent event = new NodeTypeEvent(NodeTypeEvent.REGISTER, list);
+        master.registered(event.getCollection());
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.setListener((NodeTypeEventListener) listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming a node type reregistration.
+     * @throws Exception
+     */
+    public void testNodeTypeReregistration() throws Exception {
+        NodeTypeDef ntd = new NodeTypeDef();
+        ntd.setName(NameFactoryImpl.getInstance().create("", "test"));
+        ntd.setSupertypes(new Name[]{NameConstants.NT_BASE});
+
+        ArrayList list = new ArrayList();
+        list.add(ntd);
+
+        NodeTypeEvent event = new NodeTypeEvent(NodeTypeEvent.REREGISTER, list);
+        master.reregistered(ntd);
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.setListener((NodeTypeEventListener) listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming a node type unregistration.
+     * @throws Exception
+     */
+    public void testNodeTypeUnregistration() throws Exception {
+        Name name = NameFactoryImpl.getInstance().create("", "test");
+
+        ArrayList list = new ArrayList();
+        list.add(name);
+
+        NodeTypeEvent event = new NodeTypeEvent(NodeTypeEvent.UNREGISTER, list);
+        master.unregistered(list);
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.setListener((NodeTypeEventListener) listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming a namespace registration.
+     * @throws Exception
+     */
+    public void testNamespaceRegistration() throws Exception {
+        NamespaceEvent event = new NamespaceEvent(null, "test", "http://www.test.com");
+
+        master.remapped(event.getOldPrefix(), event.getNewPrefix(), event.getUri());
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.setListener((NamespaceEventListener) listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Test producing and consuming a namespace unregistration.
+     * @throws Exception
+     */
+    public void testNamespaceUnregistration() throws Exception {
+        NamespaceEvent event = new NamespaceEvent("test", null, null);
+
+        master.remapped(event.getOldPrefix(), event.getNewPrefix(), event.getUri());
+
+        SimpleEventListener listener = new SimpleEventListener();
+        slave.setListener((NamespaceEventListener) listener);
+        slave.sync();
+
+        assertEquals(1, listener.getClusterEvents().size());
+        assertEquals(listener.getClusterEvents().get(0), event);
+    }
+
+    /**
+     * Create a cluster node, with a memory journal referencing a list of records.
+     *
+     * @param id cluster node id
+     * @param records memory journal's list of records
+     */
+    private ClusterNode createClusterNode(String id, ArrayList records)
+            throws ClusterException {
+
+        BeanConfig bc = new BeanConfig(MemoryJournal.class.getName(), new Properties());
+        JournalConfig jc = new JournalConfig(bc);
+        ClusterConfig cc = new ClusterConfig(id, SYNC_DELAY, jc);
+        SimpleClusterContext context = new SimpleClusterContext(cc, null);
+
+        ClusterNode clusterNode = new ClusterNode();
+        clusterNode.init(context);
+        if (records != null) {
+            ((MemoryJournal) clusterNode.getJournal()).setRecords(records);
+        }
+        return clusterNode;
+    }
+
+    /**
+     * Create a node state.
+     *
+     * @return node state
+     */
+    private NodeState createNodeState() {
+        Name ntName = nameFactory.create("{}testnt");
+        NodeState n = new NodeState(
+                new NodeId(UUID.randomUUID()), ntName,
+                ROOT_NODE_ID, NodeState.STATUS_EXISTING, false);
+        n.setMixinTypeNames(Collections.EMPTY_SET);
+        return n;
+    }
+
+    /**
+     * Create a property state.
+     *
+     * @param parentId parent node id
+     * @param name property name
+     */
+    private PropertyState createPropertyState(NodeId parentId, String name) {
+        Name propName = nameFactory.create(name);
+        return new PropertyState(
+                new PropertyId(parentId, propName),
+                NodeState.STATUS_EXISTING, false);
+    }
+
+    /**
+     * Create an event state for an operation on a node.
+     *
+     * @param n node state
+     * @param type <code>Event.NODE_ADDED</code> or <code>Event.NODE_REMOVED</code>
+     * @param name node name
+     * @return event state
+     */
+    private EventState createEventState(NodeState n, int type, String name) {
+        Path.Element relPath = pathFactory.createElement(nameFactory.create(name));
+
+        switch (type) {
+        case Event.NODE_ADDED:
+            return EventState.childNodeAdded(
+                    n.getParentId(), pathFactory.getRootPath(),
+                    n.getNodeId(), relPath, n.getNodeTypeName(),
+                    n.getMixinTypeNames(), session);
+        case Event.NODE_REMOVED:
+            return EventState.childNodeRemoved(
+                    n.getParentId(), pathFactory.getRootPath(),
+                    n.getNodeId(), relPath, n.getNodeTypeName(),
+                    n.getMixinTypeNames(), session);
+        }
+        return null;
+    }
+
+    /**
+     * Create an event state for a property operation.
+     *
+     * @param n node state
+     * @param type <code>Event.NODE_ADDED</code> or <code>Event.NODE_REMOVED</code>
+     * @param name property name
+     * @return event state
+     */
+    private EventState createEventState(PropertyState p, NodeState parent, int type) {
+        Path.Element relPath = pathFactory.createElement(p.getName());
+
+        switch (type) {
+        case Event.PROPERTY_ADDED:
+            return EventState.propertyAdded(
+                    p.getParentId(), pathFactory.getRootPath(), relPath,
+                    parent.getNodeTypeName(), parent.getMixinTypeNames(),
+                    session);
+        case Event.PROPERTY_CHANGED:
+            return EventState.propertyChanged(
+                    p.getParentId(), pathFactory.getRootPath(), relPath,
+                    parent.getNodeTypeName(), parent.getMixinTypeNames(),
+                    session);
+        case Event.PROPERTY_REMOVED:
+            return EventState.propertyRemoved(
+                    p.getParentId(), pathFactory.getRootPath(), relPath,
+                    parent.getNodeTypeName(), parent.getMixinTypeNames(),
+                    session);
+        }
+        return null;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterRecordTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterTest.java?rev=702459&r1=702458&r2=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/ClusterTest.java Tue Oct  7 05:43:55 2008
@@ -23,6 +23,7 @@
 import org.apache.jackrabbit.core.config.BeanConfig;
 import org.apache.jackrabbit.core.config.ClusterConfig;
 import org.apache.jackrabbit.core.config.JournalConfig;
+import org.apache.jackrabbit.core.journal.MemoryJournal;
 import org.apache.jackrabbit.test.JUnitTest;
 
 /**
@@ -30,10 +31,14 @@
  */
 public class ClusterTest extends JUnitTest {
 
-    /** Repository home value. */
+    /**
+     * Repository home value.
+     */
     private static final String REPOSITORY_HOME = "target/repository_for_test";
 
-    /** Repository home. */
+    /**
+     * Repository home.
+     */
     private File repositoryHome;
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleClusterContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleClusterContext.java?rev=702459&r1=702458&r2=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleClusterContext.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleClusterContext.java Tue Oct  7 05:43:55 2008
@@ -21,18 +21,29 @@
 import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.config.ClusterConfig;
+import org.apache.jackrabbit.core.nodetype.xml.SimpleNamespaceRegistry;
 import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.namespace.RegistryNamespaceResolver;
 
 /**
  * Simple cluster context, providing only limited functionality.
  */
 public class SimpleClusterContext implements ClusterContext {
 
-    /** Cluster config. */
-    private ClusterConfig cc;
+    /**
+     * Cluster config.
+     */
+    private final ClusterConfig cc;
 
-    /** Repository home. */
-    private File repositoryHome;
+    /**
+     * Repository home.
+     */
+    private final File repositoryHome;
+
+    /**
+     * Namespace resolver.
+     */
+    private final NamespaceResolver nsResolver;
 
     /**
      * Create a new instance of this class.
@@ -43,8 +54,12 @@
     public SimpleClusterContext(ClusterConfig cc, File repositoryHome) {
         this.cc = cc;
         this.repositoryHome = repositoryHome;
+
+        nsResolver = new RegistryNamespaceResolver(new SimpleNamespaceRegistry());
     }
 
+    //----------------------------------------------------------- ClusterContext
+
     /**
      * {@inheritDoc}
      */
@@ -56,7 +71,7 @@
      * {@inheritDoc}
      */
     public NamespaceResolver getNamespaceResolver() {
-        return null;
+        return nsResolver;
     }
 
     /**
@@ -69,10 +84,14 @@
     /**
      * {@inheritDoc}
      */
-    public void lockEventsReady(String workspace) throws RepositoryException {}
+    public void lockEventsReady(String workspace) throws RepositoryException {
+        // nothing to be done here
+    }
 
     /**
      * {@inheritDoc}
      */
-    public void updateEventsReady(String workspace) throws RepositoryException {}
+    public void updateEventsReady(String workspace) throws RepositoryException {
+        // nothing to be done here
+    }
 }

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java?rev=702459&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java Tue Oct  7 05:43:55 2008
@@ -0,0 +1,597 @@
+/*
+ * 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.core.cluster;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+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;
+import org.apache.jackrabbit.core.state.ItemState;
+
+/**
+ * Simple event listener that can be registered for all cluster event listener
+ * types and records external events in an array list.
+ */
+public class SimpleEventListener implements LockEventListener,
+        NodeTypeEventListener, NamespaceEventListener, UpdateEventListener {
+
+    /**
+     * List of cluster events received.
+     */
+    public List clusterEvents = new ArrayList();
+
+    //-------------------------------------------------------- LockEventListener
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalLock(NodeId nodeId, boolean isDeep, String userId)
+            throws RepositoryException {
+
+        clusterEvents.add(new LockEvent(nodeId, isDeep, userId));
+    }
+
+    /**
+     * Lock event auxiliary class.
+     */
+    public static class LockEvent {
+
+        /**
+         * Node id.
+         */
+        private final NodeId nodeId;
+
+        /**
+         * Deep flag.
+         */
+        private final boolean isDeep;
+
+        /**
+         * User id.
+         */
+        private final String userId;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param nodeId node id
+         * @param isDeep deep flag
+         * @param userId user id
+         */
+        public LockEvent(NodeId nodeId, boolean isDeep, String userId) {
+            this.nodeId = nodeId;
+            this.isDeep = isDeep;
+            this.userId = userId;
+        }
+
+        /**
+         * Return the node id.
+         *
+         * @return the node id
+         */
+        public NodeId getNodeId() {
+            return nodeId;
+        }
+
+        /**
+         * Return a flag indicating whether the lock is deep.
+         *
+         * @return <code>true</code> if the lock is deep;
+         *         <code>false</code> otherwise
+         */
+        public boolean isDeep() {
+            return isDeep;
+        }
+
+        /**
+         * Return the user owning the lock.
+         *
+         * @return user id
+         */
+        public String getUserId() {
+            return userId;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int hashCode() {
+            return nodeId.hashCode() ^ userId.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof LockEvent) {
+                LockEvent other = (LockEvent) obj;
+                return nodeId.equals(other.nodeId) &&
+                    isDeep == other.isDeep &&
+                    userId.equals(other.userId);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalUnlock(NodeId nodeId) throws RepositoryException {
+        clusterEvents.add(new UnlockEvent(nodeId));
+    }
+
+    /**
+     * Unlock event auxiliary class.
+     */
+    public static class UnlockEvent {
+
+        /**
+         * Node id.
+         */
+        private final NodeId nodeId;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param nodeId node id
+         */
+        public UnlockEvent(NodeId nodeId) {
+            this.nodeId = nodeId;
+        }
+
+        /**
+         * Return the node id.
+         *
+         * @return node id
+         */
+        public NodeId getNodeId() {
+            return nodeId;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int hashCode() {
+            return nodeId.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof UnlockEvent) {
+                UnlockEvent other = (UnlockEvent) obj;
+                return nodeId.equals(other.nodeId);
+            }
+            return false;
+        }
+    }
+
+    //---------------------------------------------------- NodeTypeEventListener
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalRegistered(Collection ntDefs)
+            throws RepositoryException, InvalidNodeTypeDefException {
+
+        clusterEvents.add(new NodeTypeEvent(NodeTypeEvent.REGISTER, ntDefs));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalReregistered(NodeTypeDef ntDef)
+            throws NoSuchNodeTypeException, InvalidNodeTypeDefException,
+            RepositoryException {
+
+        ArrayList ntDefs = new ArrayList();
+        ntDefs.add(ntDef);
+
+        clusterEvents.add(new NodeTypeEvent(NodeTypeEvent.REREGISTER, ntDefs));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalUnregistered(Collection ntNames)
+            throws RepositoryException, NoSuchNodeTypeException {
+
+        clusterEvents.add(new NodeTypeEvent(NodeTypeEvent.UNREGISTER, ntNames));
+    }
+
+    /**
+     * Node type event auxiliary class.
+     */
+    public static class NodeTypeEvent {
+
+        /**
+         * Operation type: registration.
+         */
+        public static final int REGISTER = NodeTypeRecord.REGISTER;
+
+        /**
+         * Operation type: re-registration.
+         */
+        public static final int REREGISTER = NodeTypeRecord.REREGISTER;
+
+        /**
+         * Operation type: unregistration.
+         */
+        public static final int UNREGISTER = NodeTypeRecord.UNREGISTER;
+
+        /**
+         * Operation.
+         */
+        private int operation;
+
+        /**
+         * Collection of node type definitions or node type names.
+         */
+        private Collection collection;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param operation operation
+         * @param collection collection of node type definitions or node
+         *                   type names
+         */
+        public NodeTypeEvent(int operation, Collection collection) {
+            this.operation = operation;
+            this.collection = collection;
+        }
+
+        /**
+         * Return the operation.
+         *
+         * @return operation
+         */
+        public int getOperation() {
+            return operation;
+        }
+
+        /**
+         * Return the collection.
+         *
+         * @return collection
+         */
+        public Collection getCollection() {
+            return collection;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int hashCode() {
+            return operation ^ collection.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof NodeTypeEvent) {
+                NodeTypeEvent other = (NodeTypeEvent) obj;
+                return operation == other.operation &&
+                    SimpleEventListener.equals(collection, other.collection);
+            }
+            return false;
+        }
+    }
+
+    //--------------------------------------------------- NamespaceEventListener
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalRemap(String oldPrefix, String newPrefix, String uri)
+            throws RepositoryException {
+
+        clusterEvents.add(new NamespaceEvent(oldPrefix, newPrefix, uri));
+    }
+
+    /**
+     * Namespace event auxiliary class.
+     */
+    public static class NamespaceEvent {
+
+        /**
+         * Old prefix.
+         */
+        private final String oldPrefix;
+
+        /**
+         * New prefix.
+         */
+        private final String newPrefix;
+
+        /**
+         * URI.
+         */
+        private final String uri;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param oldPrefix old prefix
+         * @param newPrefix new prefix
+         * @param uri URI
+         */
+        public NamespaceEvent(String oldPrefix, String newPrefix, String uri) {
+            this.oldPrefix = oldPrefix;
+            this.newPrefix = newPrefix;
+            this.uri = uri;
+        }
+
+        /**
+         * Return the old prefix.
+         *
+         * @return old prefix
+         */
+        public String getOldPrefix() {
+            return oldPrefix;
+        }
+
+        /**
+         * Return the new prefix.
+         *
+         * @return new prefix
+         */
+        public String getNewPrefix() {
+            return newPrefix;
+        }
+
+        /**
+         * Return the URI.
+         *
+         * @return URI
+         */
+        public String getUri() {
+            return uri;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int hashCode() {
+             int hashCode = 0;
+             if (oldPrefix != null) {
+                 hashCode ^= oldPrefix.hashCode();
+             }
+             if (newPrefix != null) {
+                 hashCode ^= newPrefix.hashCode();
+             }
+             if (uri != null) {
+                 hashCode ^= uri.hashCode();
+             }
+             return hashCode;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof NamespaceEvent) {
+                NamespaceEvent other = (NamespaceEvent) obj;
+                return SimpleEventListener.equals(oldPrefix, other.oldPrefix) &&
+                    SimpleEventListener.equals(newPrefix, other.newPrefix) &&
+                    SimpleEventListener.equals(uri, other.uri);
+            }
+            return false;
+        }
+    }
+
+    //------------------------------------------------------ UpdateEventListener
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalUpdate(ChangeLog changes, List events)
+            throws RepositoryException {
+
+        clusterEvents.add(new UpdateEvent(changes, events));
+
+    }
+
+    /**
+     * Update event auxiliary class.
+     */
+    public static class UpdateEvent implements Update {
+
+        /**
+         * Change log.
+         */
+        private final ChangeLog changes;
+
+        /**
+         * List of <code>EventState</code>s.
+         */
+        private final List events;
+
+        /**
+         * Attributes to be stored.
+         */
+        private final transient Map attributes = new HashMap();
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param changes change log
+         * @param events list of <code>EventState</code>s
+         */
+        public UpdateEvent(ChangeLog changes, List events) {
+            this.changes = changes;
+            this.events = events;
+        }
+
+        /**
+         * Return the change log.
+         *
+         * @return the change log
+         */
+        public ChangeLog getChanges() {
+            return changes;
+        }
+
+        /**
+         * Return the list of <code>EventState</code>s
+         *
+         * @return list of <code>EventState</code>s
+         */
+        public List getEvents() {
+            return events;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setAttribute(String name, Object value) {
+            attributes.put(name, value);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object getAttribute(String name) {
+            return attributes.get(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int hashCode() {
+            return changes.hashCode() ^ events.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof UpdateEvent) {
+                UpdateEvent other = (UpdateEvent) obj;
+                return SimpleEventListener.equals(changes, other.changes) &&
+                    SimpleEventListener.equals(events, other.events);
+            }
+            return false;
+        }
+
+    }
+
+    /**
+     * Return the collected cluster events.
+     *
+     * @return cluster events
+     */
+    public List getClusterEvents() {
+        return Collections.unmodifiableList(clusterEvents);
+    }
+
+    /**
+     * Check whether two objects are equals, allowing <code>null</code> values.
+     *
+     * @param o1 object 1
+     * @param o2 object 2
+     * @return <code>true</code> if they are equal; <code>false</code> otherwise
+     */
+    private static boolean equals(Object o1, Object o2) {
+        if (o1 == null) {
+            return o2 == null;
+        } else {
+            return o1.equals(o2);
+        }
+    }
+
+    /**
+     * Check whether two collections contain the same elements. Made necessary
+     * because the <code>Collections.unmodifiableXXX</code> methods do not
+     * return objects that override <code>equals</code> and <code>hashCode</code>.
+     *
+     * @param c1 collection 1
+     * @param c2 collection 2
+     * @return <code>true</code> if they are equal; <code>false</code> otherwise
+     */
+    private static boolean equals(Collection c1, Collection c2) {
+        if (c1.size() != c2.size()) {
+            return false;
+        }
+        Iterator iter1 = c1.iterator();
+        Iterator iter2 = c2.iterator();
+
+        while (iter1.hasNext()) {
+            Object o1 = iter1.next();
+            Object o2 = iter2.next();
+            if (!o1.equals(o2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check whether two changes logs contain the same elements. Not feasible
+     * by comparing the maps because <code>ItemState</code>s do not override
+     * {@link Object#equals(Object)}.
+     *
+     * @param changes1 change log
+     * @param changes2 change log
+     * @return <code>true</code> if the change logs are equals;
+     *         <code>false</code> otherwise.
+     */
+    private static boolean equals(ChangeLog changes1, ChangeLog changes2) {
+        return equals(changes1.addedStates(), changes2.addedStates()) &&
+            equals(changes1.deletedStates(), changes2.deletedStates()) &&
+            equals(changes1.modifiedStates(), changes2.modifiedStates());
+    }
+
+    /**
+     * Check whether two iterators return the same item states, where "same"
+     * means having the same <code>ItemId</code>
+     * @param iter1 first iterator
+     * @param iter2 second iterator
+     * @return <code>true</code> if the two iterators are equal;
+     *         <code>false</code> otherwise
+     */
+    private static boolean equals(Iterator iter1, Iterator iter2) {
+        for (;;) {
+            if (!iter1.hasNext() && !iter2.hasNext()) {
+                return true;
+            }
+            if (iter1.hasNext() && !iter2.hasNext()) {
+                return false;
+            }
+            if (!iter1.hasNext() && iter2.hasNext()) {
+                return false;
+            }
+            ItemState state1 = (ItemState) iter1.next();
+            ItemState state2 = (ItemState) iter2.next();
+            return state1.getId().equals(state2.getId());
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/SimpleEventListener.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/TestAll.java?rev=702459&r1=702458&r2=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/TestAll.java Tue Oct  7 05:43:55 2008
@@ -37,6 +37,7 @@
     public static Test suite() {
         TestSuite suite = new TestSuite();
 
+        suite.addTestSuite(ClusterRecordTest.class);
         suite.addTestSuite(ClusterTest.class);
 
         return suite;

Copied: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryJournal.java (from r701066, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryJournal.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryJournal.java?p2=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryJournal.java&p1=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryJournal.java&r1=701066&r2=702459&rev=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryJournal.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryJournal.java Tue Oct  7 05:43:55 2008
@@ -14,34 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.cluster;
+package org.apache.jackrabbit.core.journal;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import org.apache.jackrabbit.core.journal.AbstractJournal;
+import org.apache.jackrabbit.core.journal.AppendRecord;
 import org.apache.jackrabbit.core.journal.InstanceRevision;
-import org.apache.jackrabbit.core.journal.Journal;
 import org.apache.jackrabbit.core.journal.JournalException;
-import org.apache.jackrabbit.core.journal.RecordConsumer;
-import org.apache.jackrabbit.core.journal.RecordProducer;
+import org.apache.jackrabbit.core.journal.RecordIterator;
 import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
 
 /**
  * Memory-based journal, useful for testing purposes only.
  */
-public class MemoryJournal implements Journal {
+public class MemoryJournal extends AbstractJournal {
 
-    /** Revision. */
+    /**
+     * Revision.
+     */
     private InstanceRevision revision = new MemoryRevision();
 
-    /** Journal id. */
-    private String id;
-
     /**
-     * Return this journal's id.
-     *
-     * @return id
+     * List of records.
      */
-    public String getId() {
-        return id;
-    }
+    private ArrayList records = new ArrayList();
 
     /**
      * {@inheritDoc}
@@ -53,40 +55,198 @@
     /**
      * {@inheritDoc}
      */
-    public RecordProducer getProducer(String identifier)
+    public void init(String id, NamespaceResolver resolver)
             throws JournalException {
 
-        throw new JournalException("Not implemented");
+        super.init(id, resolver);
     }
 
     /**
      * {@inheritDoc}
      */
-    public void init(String id, NamespaceResolver resolver)
+    protected void doLock() throws JournalException {
+        // not implemented
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void append(AppendRecord record, InputStream in, int length)
             throws JournalException {
 
-        this.id = id;
+        byte[] data = new byte[length];
+        int off = 0;
+
+        while (off < data.length) {
+            try {
+                int len = in.read(data, off, data.length - off);
+                if (len < 0) {
+                    String msg = "Unexpected end of record after " + off + " bytes.";
+                    throw new JournalException(msg);
+                }
+                off += len;
+            } catch (IOException e) {
+                String msg = "I/O error after " + off + " bytes.";
+                throw new JournalException(msg, e);
+            }
+        }
+        records.add(new MemoryRecord(getId(), record.getProducerId(), data));
+        record.setRevision(records.size());
     }
 
     /**
      * {@inheritDoc}
      */
-    public void register(RecordConsumer consumer) throws JournalException {}
+    protected void doUnlock(boolean successful) {
+        // not implemented
+    }
 
     /**
      * {@inheritDoc}
      */
-    public void sync() throws JournalException {}
+    protected RecordIterator getRecords(long startRevision)
+            throws JournalException {
+
+        startRevision = Math.max(startRevision, 0);
+        long stopRevision = records.size();
+
+        return new MemoryRecordIterator(startRevision, stopRevision);
+    }
 
     /**
-     * {@inheritDoc}
+     * Set records. Used to share records between two journal implementations.
+     *
+     * @param records array list that should back up this memory journal
      */
-    public boolean unregister(RecordConsumer consumer) {
-        return false;
+    public void setRecords(ArrayList records) {
+        this.records = records;
     }
 
     /**
      * {@inheritDoc}
      */
-    public void close() {}
+    public void close() {
+        // nothing to be done here
+    }
+
+
+    /**
+     * Memory record.
+     */
+    public class MemoryRecord {
+
+        /**
+         * Journal id.
+         */
+        private String journalId;
+
+        /**
+         * Producer id.
+         */
+        private String producerId;
+
+        /**
+         * Data.
+         */
+        private byte[] data;
+
+        /**
+         * Create a new instance of this class
+         *
+         * @param journalId journal id
+         * @param producerId producer id
+         * @param data data
+         */
+        public MemoryRecord(String journalId, String producerId, byte[] data) {
+            this.journalId = journalId;
+            this.producerId = producerId;
+            this.data = data;
+        }
+
+        /**
+         * Return the journal id.
+         *
+         * @return the journal id
+         */
+        public String getJournalId() {
+            return journalId;
+        }
+
+        /**
+         * Return the producer id.
+         *
+         * @return the producer id
+         */
+        public String getProducerId() {
+            return producerId;
+        }
+
+        /**
+         * Return the data.
+         *
+         * @return data
+         */
+        public byte[] getData() {
+            return data;
+        }
+    }
+
+    /**
+     * Record iterator implementation.
+     */
+    public class MemoryRecordIterator implements RecordIterator {
+
+        /**
+         * Current revision.
+         */
+        private long revision;
+
+        /**
+         * Last revision.
+         */
+        private final long stopRevision;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param startRevision start revision
+         * @param stopRevision stop revision
+         */
+        public MemoryRecordIterator(long startRevision, long stopRevision) {
+            this.revision = startRevision;
+            this.stopRevision = stopRevision;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean hasNext() {
+            return revision < stopRevision;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Record nextRecord() throws NoSuchElementException,
+                JournalException {
+
+            int index = (int) revision;
+            MemoryRecord record = (MemoryRecord) records.get(index);
+
+            byte[] data = record.getData();
+            DataInputStream dataIn = new DataInputStream(
+                    new ByteArrayInputStream(data));
+
+            return new ReadRecord(record.getJournalId(), record.getProducerId(),
+                    ++revision, dataIn, data.length,
+                    getResolver(), getNamePathResolver());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            // nothing to be done here
+        }
+    }
 }

Copied: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryRevision.java (from r701066, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryRevision.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryRevision.java?p2=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryRevision.java&p1=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryRevision.java&r1=701066&r2=702459&rev=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cluster/MemoryRevision.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/MemoryRevision.java Tue Oct  7 05:43:55 2008
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.cluster;
+package org.apache.jackrabbit.core.journal;
 
 import org.apache.jackrabbit.core.journal.InstanceRevision;
 import org.apache.jackrabbit.core.journal.JournalException;
@@ -24,7 +24,9 @@
  */
 public class MemoryRevision implements InstanceRevision {
 
-    /** Revision. */
+    /**
+     * Revision.
+     */
     private long revision;
 
     /**
@@ -44,5 +46,7 @@
     /**
      * {@inheritDoc}
      */
-    public void close() {}
+    public void close() {
+        // nothing to be done here
+    }
 }

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java?rev=702459&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java Tue Oct  7 05:43:55 2008
@@ -0,0 +1,44 @@
+/*
+ * 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.core.journal;
+
+import junit.framework.TestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all test classes setting up test data in the workspace. This test
+ * data is specific to Jackrabbit and will probably not work in other
+ * implementations.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+
+        suite.addTestSuite(FileJournalTest.class);
+
+        return suite;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/journal/TestAll.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/nodetype/xml/SimpleNamespaceRegistry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/nodetype/xml/SimpleNamespaceRegistry.java?rev=702459&r1=702458&r2=702459&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/nodetype/xml/SimpleNamespaceRegistry.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/nodetype/xml/SimpleNamespaceRegistry.java Tue Oct  7 05:43:55 2008
@@ -26,7 +26,7 @@
  * Simple utility implementation of the NamespaceRegistry interface.
  * Used by the node type formatter test cases.
  */
-class SimpleNamespaceRegistry implements NamespaceRegistry {
+public class SimpleNamespaceRegistry implements NamespaceRegistry {
 
     /** Map from namespace prefixes to namespace URIs. */
     private final Properties prefixToURI = new Properties();