You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2007/09/19 09:19:22 UTC

svn commit: r577181 - in /directory/apacheds/trunk: core-unit/src/main/java/org/apache/directory/server/core/unit/ core/src/main/java/org/apache/directory/server/core/changelog/ server-tools/src/main/java/org/apache/directory/server/tools/commands/impo...

Author: akarasulu
Date: Wed Sep 19 00:19:21 2007
New Revision: 577181

URL: http://svn.apache.org/viewvc?rev=577181&view=rev
Log:
adding initial change log interceptor and various interfaces

Added:
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java   (with props)
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html   (with props)
Modified:
    directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractPerformanceTest.java
    directory/apacheds/trunk/server-tools/src/main/java/org/apache/directory/server/tools/commands/importcmd/ImportCommandExecutor.java

Modified: directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractPerformanceTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractPerformanceTest.java?rev=577181&r1=577180&r2=577181&view=diff
==============================================================================
--- directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractPerformanceTest.java (original)
+++ directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractPerformanceTest.java Wed Sep 19 00:19:21 2007
@@ -40,6 +40,7 @@
 import org.apache.directory.server.core.partition.PartitionNexus;
 import org.apache.directory.shared.ldap.constants.SchemaConstants;
 import org.apache.directory.shared.ldap.ldif.Entry;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
 import org.apache.directory.shared.ldap.ldif.LdifReader;
 import org.apache.directory.shared.ldap.message.AttributesImpl;
 import org.apache.directory.shared.ldap.name.LdapDN;
@@ -99,7 +100,7 @@
  */
 public class AbstractPerformanceTest extends AbstractTestCase
 {
-    private final Class subclass;
+    private final Class<?> subclass;
     private boolean isExternal = false;
     private String prepareCommand = null;
     private File outputDirectory = null;
@@ -113,7 +114,7 @@
      * 
      * @param subclass 
      */
-    protected AbstractPerformanceTest( Class subclass ) throws IOException
+    protected AbstractPerformanceTest( Class<?> subclass ) throws IOException
     {
         super( PartitionNexus.ADMIN_PRINCIPAL, "secret" );
         this.subclass = subclass;
@@ -225,9 +226,9 @@
      */
     protected boolean applyEntry( Entry entry ) throws Exception
     {
-        switch ( entry.getChangeType() )
+        switch ( entry.getChangeType().getChangeType() )
         {
-            case( Entry.ADD ):
+            case( ChangeType.ADD_ORDINAL ):
                 LdapDN ancestor = new LdapDN( testRoot.getNameInNamespace() );
                 LdapDN descendant = new LdapDN( entry.getDn() );
                 LdapDN relative = ( LdapDN ) NamespaceTools.getRelativeName( ancestor, descendant );
@@ -250,7 +251,7 @@
             LdifReader reader = new LdifReader( in );
             start = System.currentTimeMillis();
             log( "Started LDIF Import: " + ldifResource  );
-            for ( Iterator ii = reader.iterator(); ii.hasNext(); /**/ )
+            for ( Iterator<Entry> ii = reader.iterator(); ii.hasNext(); /**/ )
             {
                 long startEntry = System.currentTimeMillis();
                 if ( applyEntry( ( Entry ) ii.next() ) )

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,107 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+import java.io.Serializable;
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.ldif.Entry;
+
+
+/**
+ * A loggable directory change event.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangeLogEvent implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private final String zuluTime;
+    private final long revision;
+    private final Entry forwardLdif;
+    private final Entry reverseLdif;
+    private final LdapPrincipal committer;
+    
+    
+    /**
+     * Creates a new instance of ChangeLogEvent.
+     *
+     * @param the revision for the change
+     * @param committer the authorized user which triggered the change
+     * @param forwardLdif the LDIF representing the change forward in time
+     * @param reverseLdif an LDIF which reverts the change going reverse in time to an earlier revision
+     */
+    public ChangeLogEvent( long commitId, String zuluTime, LdapPrincipal committer, 
+        Entry forwardLdif, Entry reverseLdif )
+    {
+        this.zuluTime = zuluTime;
+        this.revision = commitId;
+        this.forwardLdif = forwardLdif;
+        this.reverseLdif = reverseLdif;
+        this.committer = committer;
+    }
+
+
+    /**
+     * @return the forwardLdif
+     */
+    public Entry getForwardLdif()
+    {
+        return forwardLdif;
+    }
+
+
+    /**
+     * @return the reverseLdif
+     */
+    public Entry getReverseLdif()
+    {
+        return reverseLdif;
+    }
+
+
+    /**
+     * @return the committer
+     */
+    public LdapPrincipal getCommitter()
+    {
+        return committer;
+    }
+
+
+    /**
+     * @return the revision
+     */
+    public long getRevision()
+    {
+        return revision;
+    }
+
+
+    /**
+     * @return the zuluTime
+     */
+    public String getZuluTime()
+    {
+        return zuluTime;
+    }
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,568 @@
+package org.apache.directory.server.core.changelog;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import org.apache.directory.server.core.DirectoryServiceConfiguration;
+import org.apache.directory.server.core.configuration.InterceptorConfiguration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+
+import org.apache.directory.shared.ldap.util.Base64;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An interceptor which maintains a change log as it intercepts changes to the
+ * directory.  It mainains a changes.log file using the LDIF format for changes.
+ * It appends changes to this file so the entire LDIF file can be loaded to 
+ * replicate the state of the server.
+ * 
+ */
+public class ChangeLogInterceptor extends BaseInterceptor implements Runnable
+{
+    /** time to wait before automatically waking up the writer thread */
+    private static final long WAIT_TIMEOUT_MILLIS = 1000;
+    private static final Logger log = LoggerFactory.getLogger( ChangeLogInterceptor.class );
+
+    /** the changes.log file's stream which we append change log messages to */
+    PrintWriter out = null;
+    /** queue of string buffers awaiting serialization to the log file */
+    Queue<StringBuffer> queue = new LinkedList<StringBuffer>();
+    /** a handle on the attributeType registry to determine the binary nature of attributes */
+    AttributeTypeRegistry registry = null;
+    /** determines if this service has been activated */
+    boolean isActive = false;
+    /** thread used to asynchronously write change logs to disk */
+    Thread writer = null;
+    
+    
+    // -----------------------------------------------------------------------
+    // Overridden init() and destroy() methods
+    // -----------------------------------------------------------------------
+
+    
+    public void init( DirectoryServiceConfiguration dsConfig, InterceptorConfiguration iConfig ) throws NamingException
+    {
+        super.init( dsConfig, iConfig );
+
+        // Get a handle on the attribute registry to check if attributes are binary
+        registry = dsConfig.getRegistries().getAttributeTypeRegistry();
+
+        // Open a print stream to use for flushing LDIFs into
+        File changes = new File( dsConfig.getStartupConfiguration().getWorkingDirectory(), "changes.log" );
+        try
+        {
+            if ( changes.exists() )
+            {
+                out = new PrintWriter( new FileWriter( changes, true ) );
+            }
+            else
+            {
+                out = new PrintWriter( new FileWriter( changes ) );
+            }
+        }
+        catch( Exception e )
+        {
+            log.error( "Failed to open the change log file: " + changes, e );
+        }
+        
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.println( "# Initializing changelog service: " + DateUtils.getGeneralizedTime() );
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.flush();
+        
+        writer = new Thread( this );
+        isActive = true;
+        writer.start();
+    }
+    
+    
+    public void destroy()
+    {
+        // Gracefully stop writer thread and push remaining enqueued buffers ourselves
+        isActive = false;
+        do
+        {
+            // Let's notify the writer thread to make it die faster
+            synchronized( queue )
+            {
+                queue.notifyAll();
+            }
+            
+            // Sleep tiny bit waiting for the writer to die
+            try
+            {
+                Thread.sleep( 50 );
+            }
+            catch ( InterruptedException e )
+            {
+                log.error( "Failed to sleep while waiting for writer to die", e );
+            }
+        } while ( writer.isAlive() );
+        
+        // Ok lock down queue and start draining it
+        synchronized( queue )
+        {
+            while ( ! queue.isEmpty() )
+            {
+                StringBuffer buf = queue.poll();
+                if ( buf != null )
+                {
+                    out.println( buf );
+                }
+            }
+        }
+
+        // Print message that we're stopping log service, flush and close
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.println( "# Deactivating changelog service: " + DateUtils.getGeneralizedTime() );
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.flush();
+        out.close();
+        
+        super.destroy();
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Implementation for Runnable.run() for writer Thread
+    // -----------------------------------------------------------------------
+
+    
+    public void run()
+    {
+        while ( isActive )
+        {
+            StringBuffer buf = null;
+
+            // Grab semphore to queue and dequeue from it
+            synchronized( queue )
+            {
+                try 
+                { 
+                    queue.wait( WAIT_TIMEOUT_MILLIS ); 
+                } 
+                catch ( InterruptedException e ) 
+                { 
+                    log.error( "Failed to to wait() on queue", e ); 
+                }
+                
+                buf = queue.poll();
+                queue.notifyAll();
+            }
+            
+            // Do writing outside of synch block to allow other threads to enqueue
+            if ( buf != null )
+            {
+                out.println( buf );
+                out.flush();
+            }
+        }
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Overridden (only change inducing) intercepted methods
+    // -----------------------------------------------------------------------
+
+    
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        StringBuffer buf;
+        next.add( opContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! creatorsName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! createTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF entry now
+        buf.append( "\ndn: " );
+        buf.append( opContext.getDn().getUpName() );
+        buf.append( "\nchangetype: add" );
+        append( buf, opContext.getEntry() );
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        StringBuffer buf;
+        next.delete( opContext );
+
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! deletorsName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! deleteTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( opContext.getDn() );
+        buf.append( "\nchangetype: delete\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        StringBuffer buf;
+        next.modify( opContext );
+
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! modifiersName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! modifyTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( opContext.getDn() );
+        buf.append( "\nchangetype: modify" );
+
+        ModificationItem[] mods = opContext.getModItems();
+        for ( int ii = 0; ii < mods.length; ii++ )
+        {
+            append( buf, mods[ii].getAttribute(), getModOpStr( mods[ii].getModificationOp() ) );
+        }
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Though part left as an exercise (Not Any More!)
+    // -----------------------------------------------------------------------
+
+    
+    public void rename ( NextInterceptor next, RenameOperationContext renameContext ) throws NamingException
+    {
+        next.rename( renameContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuffer buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( renameContext.getDn() );
+        buf.append( "\nchangetype: modrdn" );
+        buf.append( "\nnewrdn: " + renameContext.getNewRdn() );
+        buf.append( "\ndeleteoldrdn: " + ( renameContext.getDelOldDn() ? "1" : "0" ) );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameOperationContext )
+        throws NamingException
+    {
+        next.moveAndRename( moveAndRenameOperationContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuffer buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( moveAndRenameOperationContext.getDn() );
+        buf.append( "\nchangetype: modrdn" ); // FIXME: modrdn --> moddn ?
+        buf.append( "\nnewrdn: " + moveAndRenameOperationContext.getNewRdn() );
+        buf.append( "\ndeleteoldrdn: " + ( moveAndRenameOperationContext.getDelOldDn() ? "1" : "0" ) );
+        buf.append( "\nnewsperior: " + moveAndRenameOperationContext.getParent() );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void move ( NextInterceptor next, MoveOperationContext moveOperationContext ) throws NamingException
+    {
+        next.move( moveOperationContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuffer buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuffer();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( moveOperationContext.getDn() );
+        buf.append( "\nchangetype: moddn" ); 
+        buf.append( "\nnewsperior: " + moveOperationContext.getParent() );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Private utility methods used by interceptor methods
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Appends an Attribute and its values to a buffer containing an LDIF entry taking
+     * into account whether or not the attribute's syntax is binary or not.
+     * 
+     * @param buf the buffer written to and returned (for chaining)
+     * @param attr the attribute written to the buffer
+     * @return the buffer argument to allow for call chaining.
+     * @throws NamingException if the attribute is not identified by the registry
+     */
+    private StringBuffer append( StringBuffer buf, Attribute attr ) throws NamingException
+    {
+        String id = ( String ) attr.getID();
+        int sz = attr.size();
+        boolean isBinary = ! registry.lookup( id ).getSyntax().isHumanReadable();
+        
+        if ( isBinary )
+        {
+            for ( int ii = 0; ii < sz; ii++  )
+            {
+                buf.append( "\n" );
+                buf.append( id );
+                buf.append( ":: " );
+                Object value = attr.get( ii );
+                String encoded;
+                if ( value instanceof String )
+                {
+                    encoded = ( String ) value;
+                    try
+                    {
+                        encoded = new String( Base64.encode( encoded.getBytes( "UTF-8" ) ) );
+                    }
+                    catch ( UnsupportedEncodingException e )
+                    {
+                        log.error( "can't convert to UTF-8: " + encoded, e );
+                    }
+                }
+                else
+                {
+                    encoded = new String( Base64.encode( ( byte[] ) attr.get( ii ) ) );
+                }
+                buf.append( encoded );
+            }
+        }
+        else
+        {
+            for ( int ii = 0; ii < sz; ii++  )
+            {
+                buf.append( "\n" );
+                buf.append( id );
+                buf.append( ": " );
+                buf.append( attr.get( ii ) );
+            }
+        }
+        
+        return buf;
+    }
+    
+
+    /**
+     * Appends a set of attributes to a buffer for an LDIF record.  The Dn is presumed
+     * to be added some time before.
+     * 
+     * @param buf the buffer to add the attributes to
+     * @param attrs the attributes to append to the buffer
+     * @return the buffer argument passed in for chaining
+     * @throws NamingException if some attribute identifiers are not defined
+     */
+    private StringBuffer append( StringBuffer buf, Attributes attrs ) throws NamingException
+    {
+        NamingEnumeration<String> ids = attrs.getIDs();
+        while ( ids.hasMore() )
+        {
+            String id = ids.next();
+            append( buf, attrs.get( id ) );
+        }
+        return buf;
+    }
+
+    
+    /**
+     * Gets the DN of the user currently bound to the server executing this operation.  If 
+     * the user is anonymous "" is returned.
+     * 
+     * @return the DN of the user executing the current intercepted operation
+     * @throws NamingException if we cannot access the interceptor stack
+     */
+    private String getPrincipalName() throws NamingException
+    {
+        ServerContext ctx = ( ServerContext ) InvocationStack.getInstance().peek().getCaller();
+        return ctx.getPrincipal().getName();
+    }
+
+
+    /**
+     * Gets a String representation of the JNDI attribute modificaion flag.  Here are the mappings:
+     * <table>
+     *   <tr><th>JNDI Constant</th><th>Returned String</th></tr>
+     *   <tr><td>DirContext.ADD_ATTRIBUTE</td><td>'add: '</td></tr>
+     *   <tr><td>DirContext.REMOVE_ATTRIBUTE</td><td>'delete: '</td></tr>
+     *   <tr><td>DirContext.REPLACE_ATTRIBUTE</td><td>'replace: '</td></tr>
+     * </table>
+     * <ul><li>
+     * Note that the String in right hand column is quoted to show trailing space.
+     * </li></ul>
+     * 
+     * @param modOp the int value of the JNDI modification operation
+     * @return the string representation of the JNDI Modification operation
+     */
+    private String getModOpStr( int modOp ) 
+    {
+        String opStr;
+        switch( modOp )
+        {
+            case( DirContext.ADD_ATTRIBUTE ):
+                opStr = "add: ";
+                break;
+            case( DirContext.REMOVE_ATTRIBUTE ):
+                opStr = "delete: ";
+                break;
+            case( DirContext.REPLACE_ATTRIBUTE ):
+                opStr = "replace: ";
+                break;
+            default:
+                throw new IllegalArgumentException( "Undefined attribute modify operation: " + modOp );
+        }
+        return opStr;
+    }
+    
+
+    /**
+     * Appends a modification delta instruction to an LDIF: i.e. 
+     * <pre>
+     * add: telephoneNumber
+     * telephoneNumber: +1 408 555 1234
+     * telephoneNumber: +1 408 444 9999
+     * -
+     * </pre>
+     * 
+     * @param buf the buffer to append the attribute delta to
+     * @param mod the modified values if any for that attribute
+     * @param modOp the modification operation as a string followd by ": "
+     * @return the buffer argument provided for chaining
+     * @throws NamingException if the modification attribute id is undefined
+     */
+    private StringBuffer append( StringBuffer buf, Attribute mod, String modOp ) throws NamingException
+    {
+        buf.append( "\n" );
+        buf.append( modOp );
+        buf.append( mod.getID() );
+        append( buf, mod );
+        buf.append( "\n-" );
+        return buf;
+    }
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,227 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * A custom search engine designed for optimized searching across ChangeLogEvents
+ * within the ChangeLogStore.  The following lookup and search operations are 
+ * provided:
+ * 
+ * <ul>
+ *   <li>lookup change by revision</li>
+ *   <li>lookup change by date</li>
+ *   <li>find all changes</li>
+ *   <li>find all changes before or after a revision</li>
+ *   <li>find all changes in a revision range</li>
+ *   <li>find changes by LDAP namespace scope on DN</li>
+ *   <li>find changes by DN</li>
+ *   <li>find changes by principal</li>
+ *   <li>find changes by change type</li>
+ *   <li>find changes by attribute</li>
+ *   <li>find changes using a restricted LDAP filter on all of the above factors</li>
+ * </ul>
+ * 
+ * Note change lookups by date can be conducted by first looking up a revision 
+ * using a generalizedTime descriptor to find a revision.  Then these revisions 
+ * can be plugged into the revision based find and lookup methods.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLogSearchEngine
+{
+    /**
+     * Looks up the revision in effect at some time specified by a generalized 
+     * time descriptor.
+     *
+     * @param generalizedTime the generalized time descriptor to find the effective revision for
+     * @return the revision that was in effect at a certain time
+     * @throws NamingException if there are failures accessing the store
+     */
+    long lookup( String generalizedTime ) throws NamingException;
+    
+    
+    /**
+     * Looks up the ChangeLogEvent for a revision.
+     *
+     * @param revision to get a ChangeLogEvent for
+     * @return the ChangeLogEvent associated with the revision
+     * @throws NamingException if there are failures accessing the store
+     */
+    ChangeLogEvent lookup( long revision ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents within the system since revision 0.
+     * 
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents 
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( RevisionOrder order ) throws NamingException;
+    
+
+    /**
+     * Finds the ChangeLogEvents that occurred before a revision inclusive.
+     * 
+     * @param revision the revision number to get the ChangeLogEvents before
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents before and including some revision
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> findBefore( long revision, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds the ChangeLogEvents that occurred after a revision inclusive.
+     * 
+     * @param revision the revision number to get the ChangeLogEvents after
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents after and including the revision
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> findAfter( long revision, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds the ChangeLogEvents that occurred between a revision range inclusive.
+     * 
+     * @param startRevision the revision number to start getting the ChangeLogEvents above
+     * @param endRevision the revision number to start getting the ChangeLogEvents below
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents within some revision range inclusive
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( long startRevision, long endRevision, RevisionOrder order ) 
+        throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents on an entry.
+     *
+     * @param dn the normalized DN of the entry to get ChangeLogEvents for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that occurred on an entry
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapDN dn, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents on an entry base and/or it's children/descendants.
+     *
+     * @param base the normalized DN of the entry base to get ChangeLogEvents for
+     * @param scope the scope of the search under the base similar to LDAP search scope
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that occurred on an entry and/or it's descendants depending on the scope
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapDN base, Scope scope, RevisionOrder order ) throws NamingException;
+    
+
+    /**
+     * Finds all the ChangeLogEvents triggered by a principal in the system.
+     *
+     * @param principal the LDAP principal who triggered the events
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that were triggered by a specific LDAP user
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapPrincipal principal, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents of a particular change type.
+     * 
+     * @param changeType the change type of the ChangeLogEvents to search for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents of a particular ChangeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ChangeType changeType, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents altering a particular attributeType.
+     * 
+     * @param attributeType the attributeType definition for the changed attribute to search changes for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on a particular attributeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( AttributeType attributeType, RevisionOrder order ) throws NamingException;
+    
+
+    /**
+     * Finds all the ChangeLogEvents altering a particular objectClass.
+     * 
+     * @param objectClass the objectClass definition for the entries to search changes for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on a particular attributeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ObjectClass objectClass, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents matched by the filter expression tree parameter.
+     * 
+     * The following attributes can be used in the constrained LDAP filter expression 
+     * tree.  The expression must be normalized and can contain only ATA pairs with the 
+     * following set of attributes:
+     * 
+     * <ul>
+     *   <li>ndn: normalized distinguishedName syntax (defaults to matching a string)</li>
+     *   <li>date: generalizedTime syntax</li>
+     *   <li>revision: integer syntax</li>
+     *   <li>attributeType: numeric OID</li>
+     *   <li>objectClass: numeric OID</li>
+     *   <li>changeType: new changeType syntax</li>
+     *   <li>principal: normalized distinguishedName syntax (defaults to matching a string)</li>
+     * </ul>
+     * 
+     * The following are the only kinds of AVA node types allowed:
+     * 
+     * <ul>
+     *   <li>equality (=) </li>
+     *   <li>greaterThanEq (>=) </li>
+     *   <li>lessThanEq (<=) </li>
+     *   <li>scope (specialized) </li>
+     * </ul>
+     * 
+     * @param objectClass the objectClass definition for the entries to search changes for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on entries of a particular objectClass
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ExprNode filter, RevisionOrder order ) throws NamingException;
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,32 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+/**
+ * TODO ChangeLogService.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLogService
+{
+
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogService.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,55 @@
+/*
+ *   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.directory.server.core.changelog;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.ldif.Entry;
+
+
+/**
+ * A store for change events on the directory which exposes methods for 
+ * managing, querying and in general performing legal operations on the log.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLogStore 
+{
+    /**
+     * Get's the current revision of the server (a.k.a. the HEAD revision).
+     *
+     * @return the current revision of the server
+     */
+    long getCurrentRevision();
+    
+    /**
+     * Records a change as a forward LDIF, a reverse change to revert the change and
+     * the authorized principal triggering the revertable change event.
+     *
+     * @param principal the authorized LDAP principal triggering the change
+     * @param forward LDIF of the change going to the next state
+     * @param reverse LDIF (anti-operation): the change required to revert this change
+     * @return the commit id or revision representing the change within the log
+     * @throws NamingException
+     */
+    long log( LdapPrincipal principal, Entry forward, Entry reverse ) throws NamingException;
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,52 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+/**
+ * The order, based on revision numbers, in which to return log changes or 
+ * tags.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum RevisionOrder
+{
+    AscendingOrder( true ),
+    DescendingOrder( false );
+    
+
+    private final boolean ascending;
+    
+
+    private RevisionOrder( boolean ascending )
+    {
+        this.ascending = ascending;
+    }
+
+
+    /**
+     * @return the ascending
+     */
+    public boolean isAscending()
+    {
+        return ascending;
+    }
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,55 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * TODO Scope.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum Scope
+{
+    Subtree( SearchControls.SUBTREE_SCOPE ),
+    OneLevel( SearchControls.ONELEVEL_SCOPE ),
+    Object( SearchControls.OBJECT_SCOPE );
+    
+    
+    private final int scope;
+    
+    
+    private Scope( int scope )
+    {
+        this.scope = scope;
+    }
+
+
+    /**
+     * @return the scope
+     */
+    public int getScope()
+    {
+        return scope;
+    }
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,37 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+/**
+ * TODO SearchableChangeLogStore.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface SearchableChangeLogStore extends ChangeLogStore
+{
+    /**
+     * Get's the search engine for this SearchableChangeLogStore.
+     * 
+     * @return the search engine for this SearchableChangeLogStore
+     */
+    ChangeLogSearchEngine getChangeLogSearchEngine();
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,63 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+/**
+ * A tag on a revision representing a snapshot of the directory server's 
+ * state.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Tag
+{
+    /*
+     * TODO should we have date in which tag was taken
+     * TODO should we have the date of the revision
+     */
+    private final long revision;
+    private final String description;
+    
+    
+    public Tag( long revision, String description )
+    {
+        this.revision = revision;
+        this.description = description;
+    }
+
+
+    /**
+     * @return the revision
+     */
+    public long getRevision()
+    {
+        return revision;
+    }
+
+
+    /**
+     * @return the description
+     */
+    public String getDescription()
+    {
+        return description;
+    }
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,129 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * An optional search interface supported by TaggableChangeLogStores.  This 
+ * interface enables the:
+ * 
+ * <ul>
+ *   <li>lookup of tags by revision</li>
+ *   <li>finding all tags</li>
+ *   <li>finding tags before or after a revision</li>
+ *   <li>finding tags in some revision range</li>
+ * </ul>
+ * 
+ * While investigating these interface methods keep in mind that only one 
+ * tag can exist on a revision.  Unlike subversion which allows multiple 
+ * tags on a revision we only allow at most one: more than one is pointless.
+ * 
+ * Date wise searches for tags are not present within this interface since 
+ * they should be used in conjunction with the ChangeLogSearchEngine which
+ * is by default supported by TaggableSearchableChangeLogStores.  The 
+ * ChangeLogSearchEngine can find revisions based on time descriptors and
+ * returned revisions can be checked for the presence of tags using this
+ * interface.  The whole point to enabling both search engines in a single
+ * interfaces is because if you can search for tags you should be able to 
+ * search for revisions: however the converse may not be the case.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TagSearchEngine
+{
+
+    // -----------------------------------------------------------------------
+    // Tag Lookup Operations
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Gets the tag for a specific snapshot if that snapshot exists. 
+     *
+     * @param revision the revision number to use to check for a snapshot
+     * @return the snapshot at the revision if one exists, otherwise null
+     * @throws NamingException if there is a problem accessing the store
+     */
+    Tag lookup( long revision ) throws NamingException;
+    
+    /**
+     * Checks to see if a snapshot exists for a specific revision. 
+     *
+     * @param revision the revision number to use to check for a snapshot
+     * @return true if a snapshot exists at the revision, false otherwise
+     * @throws NamingException if there is a problem accessing the store
+     */
+    boolean has( long revision ) throws NamingException;
+
+
+    // -----------------------------------------------------------------------
+    // Tag Search Operations
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Finds all the snapshot tags taken since revision 0 until the current 
+     * revision.
+     *
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken since revision 0
+     * @throws NamingException if there is a problem accessing the store
+     */
+    NamingEnumeration<Tag> find( RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Finds all the snapshot tags taken before a specific revision.  If a tag 
+     * exists at the revision parameter it will be returned as well.
+     *
+     * @param revision the revision number to get snapshots before 
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken before a revision inclusive
+     * @throws NamingException if there is a problem accessing the store
+     */
+    NamingEnumeration<Tag> findBefore( long revision, RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Finds all the snapshot tags taken after a specific revision.  If a tag 
+     * exists at the revision parameter it will be returned as well.
+     *
+     * @param revision the revision number to get snapshots after
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken after a revision inclusive
+     * @throws NamingException if there is a problem accessing the store
+     */
+    NamingEnumeration<Tag> findAfter( long revision, RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Enumerates over the tags of all snapshots taken between a specific revision 
+     * range inclusive.  The first revision parameter should be less than or equal 
+     * to the second revision parameter.
+     *
+     * @param order the revision order in which to return snapshot tags 
+     * @return enumeration over all the snapshots taken in a revision range inclusive
+     * @throws NamingException if there is a problem accessing the store
+     */
+    NamingEnumeration<Tag> find( long startRevision, long endRevision, RevisionOrder order ) 
+        throws NamingException;
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,50 @@
+/*
+ *   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.directory.server.core.changelog;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A ChangeLogStore which allows tagging for tracking server state snapshots.
+ * At most one tag per revision can be created.  There is no point to creating
+ * more than one tag on a revision in our case for snapshotting server state.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TaggableChangeLogStore extends ChangeLogStore
+{
+    /**
+     * Creates a tag for a snapshot of the server in a specific state at a revision.
+     *
+     * @param the revision to tag the snapshot
+     * @throws NamingException
+     */
+    Tag tag( long revision ) throws NamingException;
+
+    /**
+     * Creates a snapshot of the server at the current revision.
+     *
+     * @return the revision at which the tag is created
+     * @throws NamingException
+     */
+    Tag tag() throws NamingException;
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java Wed Sep 19 00:19:21 2007
@@ -0,0 +1,37 @@
+/*
+ *   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.directory.server.core.changelog;
+
+
+/**
+ * TODO TaggableSearchableChangeLogStore.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TaggableSearchableChangeLogStore extends TaggableChangeLogStore, SearchableChangeLogStore
+{
+    /**
+     * Get's the tag search engine for this TaggableSearchableChangeLogStore.
+     *
+     * @return the snapshot query engine for this store.
+     */
+    TagSearchEngine getTagSearchEngine();
+}

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html?rev=577181&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html Wed Sep 19 00:19:21 2007
@@ -0,0 +1,28 @@
+
+
+<h1>Ideas</h1>
+
+Use AdminModel to control what goes into the changelog ?  Or use admin model to identify 
+scopes/concerns which are put into different channels in the changelog?  Cool idea perhaps,
+perhaps not.  To some degree the DN does the scope thingy for us.  There really is no point
+to having an additional scope parameter.
+
+Perhaps we can also inject a new revisions (multi-valued) operational attribute into 
+entries to track the revisions of changes in the changeLog to that entry.  This can
+be used to ask the server for a log of changes that have been performed on a specific 
+entry.  Whoa that's really hot for auditing!
+
+We could try to do the same thing (meaning having a tags operational attribute) with revisions.
+However this is pointless since the tag revision would already be in the revisions attribute.  Also
+a tag would select entries dynamically: all entries with revisions below the tag revision would be
+selected in the tag.  This leads to a neat idea: you can easily regenerate not only the revision 
+history of an entry, you can do it for an entire subtree, and furthermore you might even be able
+to conduct search operations based on a tag and the state of the server in the past.  This would be
+pretty wild.
+
+Another neat thing that could be done is to request an attribute by revision using the protocol 
+based tagging mechanism in LDAP.  For example we have language based tags like cn;lang-en so why
+not have version based tags like cn;revision-23.  When requested in this mannar the server can 
+reconstruct the state of the attribute at a specific revision and return it to the user.  This is
+an incredible capability when storing the configurations of systems in LDAP.  Being able to rollback
+to a previous configuration or just inquire about a previous state is a powerful feature to have.

Propchange: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/changelog/package.html
------------------------------------------------------------------------------
    svn:executable = *

Modified: directory/apacheds/trunk/server-tools/src/main/java/org/apache/directory/server/tools/commands/importcmd/ImportCommandExecutor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-tools/src/main/java/org/apache/directory/server/tools/commands/importcmd/ImportCommandExecutor.java?rev=577181&r1=577180&r2=577181&view=diff
==============================================================================
--- directory/apacheds/trunk/server-tools/src/main/java/org/apache/directory/server/tools/commands/importcmd/ImportCommandExecutor.java (original)
+++ directory/apacheds/trunk/server-tools/src/main/java/org/apache/directory/server/tools/commands/importcmd/ImportCommandExecutor.java Wed Sep 19 00:19:21 2007
@@ -67,6 +67,7 @@
 import org.apache.directory.shared.ldap.codec.modify.ModifyRequest;
 import org.apache.directory.shared.ldap.codec.modifyDn.ModifyDNRequest;
 import org.apache.directory.shared.ldap.codec.unbind.UnBindRequest;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
 import org.apache.directory.shared.ldap.ldif.Entry;
 import org.apache.directory.shared.ldap.ldif.LdifReader;
 import org.apache.directory.shared.ldap.message.ModificationItemImpl;
@@ -516,20 +517,20 @@
     private int changeEntry( Entry entry, int messageId ) throws IOException, DecoderException, InvalidNameException,
         NamingException, EncoderException
     {
-        switch ( entry.getChangeType() )
+        switch ( entry.getChangeType().getChangeType() )
         {
-            case Entry.ADD:
+            case ChangeType.ADD_ORDINAL:
                 // No difference with the injection of new entries
                 return addEntry( entry, messageId );
 
-            case Entry.DELETE:
+            case ChangeType.DELETE_ORDINAL:
                 return deleteEntry( entry, messageId );
 
-            case Entry.MODIFY:
+            case ChangeType.MODIFY_ORDINAL:
                 return changeModifyEntry( entry, messageId );
 
-            case Entry.MODDN:
-            case Entry.MODRDN:
+            case ChangeType.MODDN_ORDINAL:
+            case ChangeType.MODRDN_ORDINAL:
                 return changeModRDNEntry( entry, messageId );
 
             default: