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 2006/12/05 17:47:04 UTC

svn commit: r482707 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: ./ cluster/ nodetype/

Author: dpfister
Date: Tue Dec  5 08:47:02 2006
New Revision: 482707

URL: http://svn.apache.org/viewvc?view=rev&rev=482707
Log:
JCR-623 - Clustering
+ Transmit namespace registrations
+ Transmit nodetype registrations

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventChannel.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventListener.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventChannel.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventListener.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileJournal.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordInput.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordOutput.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/Journal.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/RecordProcessor.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java Tue Dec  5 08:47:02 2006
@@ -18,6 +18,8 @@
 
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.jackrabbit.core.cluster.NamespaceEventChannel;
+import org.apache.jackrabbit.core.cluster.NamespaceEventListener;
 import org.apache.jackrabbit.name.AbstractNamespaceResolver;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.name.NameCache;
@@ -41,7 +43,7 @@
  * A <code>NamespaceRegistryImpl</code> ...
  */
 public class NamespaceRegistryImpl extends AbstractNamespaceResolver
-        implements NamespaceRegistry, NameCache {
+        implements NamespaceRegistry, NameCache, NamespaceEventListener {
 
     private static Logger log = LoggerFactory.getLogger(NamespaceRegistryImpl.class);
 
@@ -79,6 +81,11 @@
     private final FileSystem nsRegStore;
 
     /**
+     * Namespace event channel.
+     */
+    private NamespaceEventChannel eventChannel;
+
+    /**
      * Protected constructor: Constructs a new instance of this class.
      *
      * @param nsRegStore
@@ -249,6 +256,16 @@
         }
     }
 
+    /**
+     * Set an event channel to inform about changes.
+     *
+     * @param eventChannel event channel
+     */
+    public void setEventChannel(NamespaceEventChannel eventChannel) {
+        this.eventChannel = eventChannel;
+        eventChannel.setListener(this);
+    }
+
     //----------------------------------------------------< NamespaceRegistry >
     /**
      * {@inheritDoc}
@@ -309,6 +326,10 @@
         prefixToURI.put(prefix, uri);
         uriToPrefix.put(uri, prefix);
 
+        if (eventChannel != null) {
+            eventChannel.remapped(oldPrefix, prefix, uri);
+        }
+
         // persist mappings
         store();
 
@@ -403,5 +424,46 @@
     public void evictAllNames() {
         // just delegate to internal cache
         resolver.evictAllNames();
+    }
+
+    //-----------------------------------------------< NamespaceEventListener >
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalRemap(String oldPrefix, String newPrefix, String uri)
+            throws RepositoryException {
+
+        if (newPrefix == null) {
+            /**
+             * as we can't guarantee that there are no references to the specified
+             * namespace (in names of nodes/properties/node types etc.) we simply
+             * don't allow it.
+             */
+            throw new NamespaceException("unregistering namespaces is not supported.");
+        }
+
+        if (oldPrefix != null) {
+            // remove old prefix mapping
+            prefixToURI.remove(oldPrefix);
+            uriToPrefix.remove(uri);
+        }
+
+        // add new prefix mapping
+        prefixToURI.put(newPrefix, uri);
+        uriToPrefix.put(uri, newPrefix);
+
+        // persist mappings
+        store();
+
+        // notify listeners
+        if (oldPrefix != null) {
+            // remapped existing namespace uri to new prefix
+            notifyNamespaceRemapped(oldPrefix, newPrefix, uri);
+        } else {
+            // added new namespace uri mapped to prefix
+            notifyNamespaceAdded(newPrefix, uri);
+        }
+
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Tue Dec  5 08:47:02 2006
@@ -274,6 +274,8 @@
         // will be interested in
         if (repConfig.getClusterConfig() != null) {
             clusterNode = createClusterNode();
+            nsReg.setEventChannel(clusterNode);
+            ntReg.setEventChannel(clusterNode);
         }
 
         // init version manager
@@ -365,7 +367,7 @@
         VersionManagerImpl vMgr = new VersionManagerImpl(pm, fs, ntReg, delegatingDispatcher,
                 VERSION_STORAGE_NODE_ID, SYSTEM_ROOT_NODE_ID, cacheFactory);
         if (clusterNode != null) {
-            vMgr.setEventChannel(clusterNode.createUpdateChannel());
+            vMgr.setEventChannel(clusterNode);
         }
         return vMgr;
     }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java Tue Dec  5 08:47:02 2006
@@ -21,6 +21,8 @@
 import org.apache.jackrabbit.core.config.ClusterConfig;
 import org.apache.jackrabbit.core.config.ConfigurationException;
 import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
+import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
 import org.apache.jackrabbit.core.observation.EventState;
 import org.apache.jackrabbit.core.observation.EventStateCollection;
 import org.apache.jackrabbit.core.state.ChangeLog;
@@ -36,11 +38,13 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Collection;
 
 /**
  * Default clustered node implementation.
  */
-public class ClusterNode implements Runnable, UpdateEventChannel  {
+public class ClusterNode implements Runnable, UpdateEventChannel,
+        NamespaceEventChannel, NodeTypeEventChannel  {
 
     /**
      * System property specifying a node id to use.
@@ -103,6 +107,16 @@
     private UpdateEventListener versionUpdateListener;
 
     /**
+     * Namespace listener.
+     */
+    private NamespaceEventListener namespaceListener;
+
+    /**
+     * Node type listener.
+     */
+    private NodeTypeEventListener nodeTypeListener;
+
+    /**
      * Initialize this cluster node.
      *
      * @throws ClusterException if an error occurs
@@ -240,15 +254,6 @@
     }
 
     /**
-     * Create an {@link UpdateEventChannel} for versioning operations.
-     *
-     * @return update event channel
-     */
-    public UpdateEventChannel createUpdateChannel() {
-        return this;
-    }
-
-    /**
      * Create an {@link UpdateEventChannel} for some workspace.
      *
      * @param workspace workspace name
@@ -386,6 +391,58 @@
         versionUpdateListener = listener;
     }
 
+    //-----------------------------------------------< NamespaceEventListener >
+
+    /**
+     * {@inheritDoc}
+     */
+    public void remapped(String oldPrefix, String newPrefix, String uri) {
+        try {
+            journal.begin(null);
+            journal.log(oldPrefix, newPrefix, uri);
+            journal.prepare();
+            journal.commit();
+        } catch (JournalException e) {
+            String msg = "Unable to create log entry: " + e.getMessage();
+            log.error(msg);
+        } catch (Throwable e) {
+            String msg = "Unexpected error while creating log entry.";
+            log.error(msg, e);
+        }
+    }
+
+    public void setListener(NamespaceEventListener listener) {
+        namespaceListener = listener;
+    }
+
+    //------------------------------------------------< NodeTypeEventListener >
+
+    /**
+     * {@inheritDoc}
+     */
+    public void registered(Collection ntDefs) {
+        try {
+            journal.begin(null);
+            journal.log(ntDefs);
+            journal.prepare();
+            journal.commit();
+        } catch (JournalException e) {
+            String msg = "Unable to create log entry: " + e.getMessage();
+            log.error(msg);
+        } catch (Throwable e) {
+            String msg = "Unexpected error while creating log entry.";
+            log.error(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setListener(NodeTypeEventListener listener) {
+        nodeTypeListener = listener;
+    }
+
+
     /**
      * Workspace update channel.
      */
@@ -570,7 +627,6 @@
          * {@inheritDoc}
          */
         public void process(NodeId nodeId, boolean isDeep, String owner) {
-            //todo should be aggregated
             LockEventListener listener = (LockEventListener) wspLockListeners.get(workspace);
             if (listener == null) {
                 try {
@@ -599,19 +655,18 @@
          * {@inheritDoc}
          */
         public void process(NodeId nodeId) {
-            //todo should be aggregated
             LockEventListener listener = (LockEventListener) wspLockListeners.get(workspace);
             if (listener == null) {
                 try {
                     clusterContext.lockEventsReady(workspace);
                 } catch (RepositoryException e) {
-                    String msg = "Error making lock listener for workspace " +
+                    String msg = "Unable to make lock listener for workspace " +
                             workspace + " online: " + e.getMessage();
                     log.warn(msg);
                 }
                 listener = (LockEventListener) wspLockListeners.get(workspace);
                 if (listener ==  null) {
-                    String msg = "Lock listener unavailable for workspace: " + workspace;
+                    String msg = "Lock channel unavailable for workspace: " + workspace;
                     log.error(msg);
                     return;
                 }
@@ -619,7 +674,44 @@
             try {
                 listener.externalUnlock(nodeId);
             } catch (RepositoryException e) {
-                String msg = "Unable to deliver unlock event: " + e.getMessage();
+                String msg = "Unable to deliver lock event: " + e.getMessage();
+                log.error(msg);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void process(String oldPrefix, String newPrefix, String uri) {
+            if (namespaceListener == null) {
+                String msg = "Namespace listener unavailable.";
+                log.error(msg);
+                return;
+            }
+            try {
+                namespaceListener.externalRemap(oldPrefix, newPrefix, uri);
+            } catch (RepositoryException e) {
+                String msg = "Unable to deliver namespace operation: " + e.getMessage();
+                log.error(msg);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void process(Collection ntDefs) {
+            if (nodeTypeListener == null) {
+                String msg = "NodeType listener unavailable.";
+                log.error(msg);
+                return;
+            }
+            try {
+                nodeTypeListener.externalRegistered(ntDefs);
+            } catch (InvalidNodeTypeDefException e) {
+                String msg = "Unable to deliver node type operation: " + e.getMessage();
+                log.error(msg);
+            } catch (RepositoryException e) {
+                String msg = "Unable to deliver node type operation: " + e.getMessage();
                 log.error(msg);
             }
         }
@@ -652,6 +744,7 @@
                 } else {
                     String msg = "Version update listener unavailable.";
                     log.error(msg);
+                    return;
                 }
             }
             try {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileJournal.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileJournal.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileJournal.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileJournal.java Tue Dec  5 08:47:02 2006
@@ -17,6 +17,8 @@
 package org.apache.jackrabbit.core.cluster;
 
 import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
+import org.apache.jackrabbit.core.nodetype.compact.ParseException;
 import org.apache.jackrabbit.core.state.ChangeLog;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.NodeState;
@@ -39,6 +41,7 @@
 import java.util.Iterator;
 import java.util.Set;
 import java.util.HashSet;
+import java.util.Collection;
 
 import EDU.oswego.cs.dl.util.concurrent.Mutex;
 
@@ -320,9 +323,6 @@
 
         try {
             workspace = in.readString();
-            if (workspace.equals("")) {
-                workspace = null;
-            }
             processor.start(workspace);
 
             for (;;) {
@@ -356,13 +356,26 @@
                             childRelPath, ntName, mixins, userId);
                 } else if (c == 'L') {
                     NodeId nodeId = in.readNodeId();
-                    boolean isDeep = in.readBoolean();
-                    String owner = in.readString();
-
-                    processor.process(nodeId, isDeep, owner);
-                } else if (c == 'U') {
-                    NodeId nodeId = in.readNodeId();
-                    processor.process(nodeId);
+                    boolean isLock = in.readBoolean();
+                    if (isLock) {
+                        boolean isDeep = in.readBoolean();
+                        String owner = in.readString();
+                        processor.process(nodeId, isDeep, owner);
+                    } else {
+                        processor.process(nodeId);
+                    }
+                } else if (c == 'S') {
+                    String oldPrefix = in.readString();
+                    String newPrefix = in.readString();
+                    String uri = in.readString();
+                    processor.process(oldPrefix, newPrefix, uri);
+                } else if (c == 'T') {
+                    int size = in.readInt();
+                    HashSet ntDefs = new HashSet();
+                    for (int i = 0; i < size; i++) {
+                        ntDefs.add(in.readNodeTypeDef());
+                    }
+                    processor.process(ntDefs);
                 } else {
                     throw new IllegalArgumentException("Unknown entry type: " + c);
                 }
@@ -373,6 +386,10 @@
             String msg = "Unable to read revision " + record.getRevision() +
                     ": " + e.getMessage();
             throw new JournalException(msg);
+        } catch (ParseException e) {
+            String msg = "Unable to read revision " + record.getRevision() +
+                    ": " + e.getMessage();
+            throw new JournalException(msg);
         } catch (IOException e) {
             String msg = "Unable to read revision " + record.getRevision() +
                     ": " + e.getMessage();
@@ -406,7 +423,7 @@
 
             record = new FileRecord(id, tempLog);
             out = record.getOutput(resolver);
-            out.writeString(workspace != null ? workspace : "");
+            out.writeString(workspace);
 
             succeeded = true;
         } catch (IOException e) {
@@ -461,12 +478,12 @@
     /**
      * {@inheritDoc}
      */
-    public void log(NodeId nodeId, boolean isDeep, String owner) throws JournalException {
+    public void log(String oldPrefix, String newPrefix, String uri) throws JournalException {
         try {
-            out.writeChar('L');
-            out.writeNodeId(nodeId);
-            out.writeBoolean(isDeep);
-            out.writeString(owner);
+            out.writeChar('S');
+            out.writeString(oldPrefix);
+            out.writeString(newPrefix);
+            out.writeString(uri);
         } catch (IOException e) {
             String msg = "Unable to write to journal log " + tempLog + ": " + e.getMessage();
             throw new JournalException(msg);
@@ -476,14 +493,34 @@
     /**
      * {@inheritDoc}
      */
+    public void log(NodeId nodeId, boolean isDeep, String owner) throws JournalException {
+        log(nodeId, true, isDeep, owner);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void log(NodeId nodeId) throws JournalException {
+        log(nodeId, false, false, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void log(Collection ntDefs) throws JournalException {
         try {
-            out.writeChar('U');
-            out.writeNodeId(nodeId);
+            out.writeChar('T');
+            out.writeInt(ntDefs.size());
+
+            Iterator iter = ntDefs.iterator();
+            while (iter.hasNext()) {
+                out.writeNodeTypeDef((NodeTypeDef) iter.next());
+            }
         } catch (IOException e) {
             String msg = "Unable to write to journal log " + tempLog + ": " + e.getMessage();
             throw new JournalException(msg);
         }
+
     }
 
     /**
@@ -546,6 +583,32 @@
         } catch (NoPrefixDeclaredException e) {
             String msg = "Unable to write to journal log " + tempLog + ": " + e.getMessage();
             throw new JournalException(msg);
+        } catch (IOException e) {
+            String msg = "Unable to write to journal log " + tempLog + ": " + e.getMessage();
+            throw new JournalException(msg);
+        }
+    }
+
+    /**
+     * Log either a lock or an unlock operation.
+     *
+     * @param nodeId node id
+     * @param isLock <code>true</code> if this is a lock;
+     *               <code>false</code> if this is an unlock
+     * @param isDeep flag indicating whether lock is deep
+     * @param owner lock owner
+     */
+    protected void log(NodeId nodeId, boolean isLock, boolean isDeep, String owner)
+            throws JournalException {
+
+        try {
+            out.writeChar('L');
+            out.writeNodeId(nodeId);
+            out.writeBoolean(isLock);
+            if (isLock) {
+                out.writeBoolean(isDeep);
+                out.writeString(owner);
+            }
         } catch (IOException e) {
             String msg = "Unable to write to journal log " + tempLog + ": " + e.getMessage();
             throw new JournalException(msg);

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordInput.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordInput.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordInput.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordInput.java Tue Dec  5 08:47:02 2006
@@ -18,6 +18,9 @@
 
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
+import org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader;
+import org.apache.jackrabbit.core.nodetype.compact.ParseException;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.name.NameFormat;
@@ -31,7 +34,9 @@
 
 import java.io.IOException;
 import java.io.DataInputStream;
+import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Allows reading data from a <code>FileRecord</code>.
@@ -120,13 +125,18 @@
     /**
      * Read a string from the underlying stream.
      *
-     * @return string
+     * @return string or <code>null</code>
      * @throws IOException if an I/O error occurs
      */
     public String readString() throws IOException {
         checkOpen();
 
-        return in.readUTF();
+        boolean isNull = in.readBoolean();
+        if (isNull) {
+            return null;
+        } else {
+            return in.readUTF();
+        }
     }
 
     /**
@@ -218,6 +228,24 @@
 
         return new PropertyId(readNodeId(), readQName());
     }
+
+    /**
+     * Read a <code>NodeTypeDef</code>
+     */
+    public NodeTypeDef readNodeTypeDef() throws IOException, ParseException {
+        checkOpen();
+
+        StringReader sr = new StringReader(readString());
+
+        CompactNodeTypeDefReader reader = new CompactNodeTypeDefReader(sr, "(internal)");
+        List ntds = reader.getNodeTypeDefs();
+        if (ntds.size() != 1) {
+            throw new IOException("Expected one node type definition: got " + ntds.size());
+        }
+        return (NodeTypeDef) ntds.get(0);
+    }
+
+
 
     /**
      * Close this input. Does not close underlying stream as this is a shared resource.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordOutput.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordOutput.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordOutput.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/FileRecordOutput.java Tue Dec  5 08:47:02 2006
@@ -18,6 +18,8 @@
 
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
+import org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefWriter;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.name.NameFormat;
@@ -27,6 +29,7 @@
 
 import java.io.IOException;
 import java.io.DataOutputStream;
+import java.io.StringWriter;
 import java.util.ArrayList;
 
 /**
@@ -123,13 +126,18 @@
     /**
      * Write a string from the underlying stream.
      *
-     * @param s string
+     * @param s string, may be <code>null</code>
      * @throws IOException if an I/O error occurs
      */
     public void writeString(String s) throws IOException {
         checkOpen();
 
-        out.writeUTF(s);
+        if (s == null) {
+            out.writeBoolean(true);
+        } else {
+            out.writeBoolean(false);
+            out.writeUTF(s);
+        }
     }
 
     /**
@@ -210,6 +218,20 @@
 
         writeNodeId(propertyId.getParentId());
         writeQName(propertyId.getName());
+    }
+
+    /**
+     * Write a <code>NodeTypeDef</code>
+     */
+    public void writeNodeTypeDef(NodeTypeDef ntd) throws IOException {
+        checkOpen();
+
+        StringWriter sw = new StringWriter();
+        CompactNodeTypeDefWriter writer = new CompactNodeTypeDefWriter(sw, resolver, true);
+        writer.write(ntd);
+        writer.close();
+
+        writeString(sw.toString());
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/Journal.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/Journal.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/Journal.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/Journal.java Tue Dec  5 08:47:02 2006
@@ -19,7 +19,11 @@
 import org.apache.jackrabbit.core.state.ChangeLog;
 import org.apache.jackrabbit.core.observation.EventStateCollection;
 import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
 import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.QName;
+
+import java.util.Collection;
 
 /**
  * Journal interface. Defines operations on a journal that are used to synchronize clustered repository nodes.
@@ -67,7 +71,8 @@
      * @param owner lock owner
      * @throws JournalException if an error occurs
      */
-    public void log(NodeId nodeId, boolean isDeep, String owner) throws JournalException;
+    public void log(NodeId nodeId, boolean isDeep, String owner)
+            throws JournalException;
 
     /**
      * Log an unlock operation.
@@ -76,6 +81,23 @@
      * @throws JournalException if an error occurs
      */
     public void log(NodeId nodeId) throws JournalException;
+
+    /**
+     * Log a namespace registry operation.
+     *
+     * @param oldPrefix old prefix
+     * @param newPrefix new prefix
+     * @param uri URI
+     * @throws JournalException if an error occurs
+     */
+    public void log(String oldPrefix, String newPrefix, String uri) throws JournalException;
+
+    /**
+     * Log on or more node type registrations or reregistration.
+     *
+     * @param ntDefs node type definitions
+     */
+    public void log(Collection ntDefs) throws JournalException;
 
     /**
      * Prepare an update operation on the journal. This locks the journal exclusively for updates until this client

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventChannel.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventChannel.java?view=auto&rev=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventChannel.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventChannel.java Tue Dec  5 08:47:02 2006
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Event channel used to transmit namespace registry operations.
+ */
+public interface NamespaceEventChannel {
+
+    /**
+     * Called when a namespace has been remapped.
+     *
+     * @param oldPrefix old prefix. if <code>null</code> this is a fresh mapping
+     * @param newPrefix new prefix. if <code>null</code> this is an unmap operation
+     * @param uri uri to map prefix to
+     */
+    public void remapped(String oldPrefix, String newPrefix, String uri);
+
+    /**
+     * Set listener that will receive information about incoming, external namespace events.
+     *
+     * @param listener namespace event listener
+     */
+    public void setListener(NamespaceEventListener listener);
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventListener.java?view=auto&rev=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventListener.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NamespaceEventListener.java Tue Dec  5 08:47:02 2006
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.jackrabbit.core.NodeId;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Interface used to receive information about incoming, external namespace registry events.
+ */
+public interface NamespaceEventListener {
+
+    /**
+     * Called when a namespace has been externally remapped.
+     *
+     * @param oldPrefix old prefix. if <code>null</code> this is a fresh mapping
+     * @param newPrefix new prefix. if <code>null</code> this is an unmap operation
+     * @param uri uri to map prefix to
+     * @throws RepositoryException if an error occurs
+     */
+    public void externalRemap(String oldPrefix, String newPrefix, String uri)
+            throws RepositoryException;
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventChannel.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventChannel.java?view=auto&rev=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventChannel.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventChannel.java Tue Dec  5 08:47:02 2006
@@ -0,0 +1,39 @@
+/*
+ * 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.Collection;
+
+/**
+ * Event channel used to transmit nodetype registry operations.
+ */
+public interface NodeTypeEventChannel {
+
+    /**
+     * Called when one or more node types have been registered.
+     *
+     * @param ntDefs collection of node type definitions
+     */
+    public void registered(Collection ntDefs);
+
+    /**
+     * Set listener that will receive information about incoming, external node type events.
+     *
+     * @param listener node type event listener
+     */
+    public void setListener(NodeTypeEventListener listener);
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventListener.java?view=auto&rev=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventListener.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/NodeTypeEventListener.java Tue Dec  5 08:47:02 2006
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
+
+import javax.jcr.RepositoryException;
+import java.util.Collection;
+
+/**
+ * Interface used to receive information about incoming, external node type registry events.
+ */
+public interface NodeTypeEventListener {
+
+    /**
+     * Called when one or more node types have been externally registered.
+     *
+     * @param ntDefs node type definitions
+     * @throws RepositoryException if an error occurs
+     * @throws InvalidNodeTypeDefException if the node type definition is invalid
+     */
+    public void externalRegistered(Collection ntDefs)
+            throws RepositoryException, InvalidNodeTypeDefException;
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/RecordProcessor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/RecordProcessor.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/RecordProcessor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cluster/RecordProcessor.java Tue Dec  5 08:47:02 2006
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.name.QName;
 
 import java.util.Set;
+import java.util.Collection;
 
 /**
  * Listener interface on a journal that gets called back for records that should be processed.
@@ -70,6 +71,22 @@
      * @param nodeId node id
      */
     public void process(NodeId nodeId);
+
+    /**
+     * Process a namespace operation.
+     *
+     * @param oldPrefix old prefix. if <code>null</code> this is a fresh mapping
+     * @param newPrefix new prefix. if <code>null</code> this is an unmap operation
+     * @param uri uri to map prefix to
+     */
+    public void process(String oldPrefix, String newPrefix, String uri);
+
+    /**
+     * Process one or more node type registrations.
+     *
+     * @param ntDefs node type definition
+     */
+    public void process(Collection ntDefs);
 
     /**
      * Invoked when a record ends.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java?view=diff&rev=482707&r1=482706&r2=482707
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java Tue Dec  5 08:47:02 2006
@@ -22,6 +22,8 @@
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.util.Dumpable;
 import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.core.cluster.NodeTypeEventChannel;
+import org.apache.jackrabbit.core.cluster.NodeTypeEventListener;
 import org.apache.jackrabbit.name.QName;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +51,7 @@
 /**
  * A <code>NodeTypeRegistry</code> ...
  */
-public class NodeTypeRegistry implements Dumpable {
+public class NodeTypeRegistry implements Dumpable, NodeTypeEventListener {
 
     private static Logger log = LoggerFactory.getLogger(NodeTypeRegistry.class);
 
@@ -98,6 +100,11 @@
             Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
 
     /**
+     * Node type event channel.
+     */
+    private NodeTypeEventChannel eventChannel;
+
+    /**
      * Create a new <code>NodeTypeRegistry</codes>
      *
      * @param nsReg
@@ -168,6 +175,12 @@
         customNTDefs.add(ntd);
         persistCustomNodeTypeDefs(customNTDefs);
 
+        if (eventChannel != null) {
+            HashSet ntDefs = new HashSet();
+            ntDefs.add(ntd);
+            eventChannel.registered(ntDefs);
+        }
+
         // notify listeners
         notifyRegistered(ntd.getName());
 
@@ -196,6 +209,12 @@
             customNTDefs.add(ntDef);
         }
         persistCustomNodeTypeDefs(customNTDefs);
+
+        // inform cluster
+        if (eventChannel != null) {
+            eventChannel.registered(ntDefs);
+        }
+
         // notify listeners
         for (Iterator iter = ntDefs.iterator(); iter.hasNext();) {
             NodeTypeDef ntDef = (NodeTypeDef) iter.next();
@@ -574,6 +593,29 @@
         entCache.dump(ps);
     }
 
+    //------------------------------------------------< NodeTypeEventListener >
+
+    /**
+     * {@inheritDoc}
+     */
+    public void externalRegistered(Collection ntDefs)
+            throws RepositoryException, InvalidNodeTypeDefException {
+
+        // validate and register new node type definitions
+        internalRegister(ntDefs);
+        // persist new node type definitions
+        for (Iterator iter = ntDefs.iterator(); iter.hasNext();) {
+            NodeTypeDef ntDef = (NodeTypeDef) iter.next();
+            customNTDefs.add(ntDef);
+        }
+        persistCustomNodeTypeDefs(customNTDefs);
+        // notify listeners
+        for (Iterator iter = ntDefs.iterator(); iter.hasNext();) {
+            NodeTypeDef ntDef = (NodeTypeDef) iter.next();
+            notifyRegistered(ntDef.getName());
+        }
+    }
+
     //---------------------------------------------------------< overridables >
     /**
      * Protected constructor
@@ -851,6 +893,17 @@
     public NodeDef getRootNodeDef() {
         return rootNodeDef;
     }
+
+    /**
+     * Set an event channel to inform about changes.
+     *
+     * @param eventChannel event channel
+     */
+    public void setEventChannel(NodeTypeEventChannel eventChannel) {
+        this.eventChannel = eventChannel;
+        eventChannel.setListener(this);
+    }
+
 
     /**
      * @param ntName