You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2019/04/18 22:15:36 UTC

[directory-ldap-api] branch master updated: Added support for RFC 4525 (modify increment operation)

This is an automated email from the ASF dual-hosted git repository.

elecharny pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git


The following commit(s) were added to refs/heads/master by this push:
     new a9640f8  Added support for RFC 4525 (modify increment operation)
     new 721bef1  Merge branch 'master' of https://gitbox.apache.org/repos/asf/directory-ldap-api
a9640f8 is described below

commit a9640f8e36f035152f76b3a1031a9506daefeb0f
Author: emmanuel lecharny <el...@apache.org>
AuthorDate: Fri Apr 19 00:14:54 2019 +0200

    Added support for RFC 4525 (modify increment operation)
---
 .../api/dsmlv2/request/Dsmlv2Grammar.java          |   4 +
 .../api/dsmlv2/request/ModifyRequestDsml.java      |  52 ++++++++
 .../dsmlv2/modifyRequest/ModifyRequestTest.java    |  37 ++++++
 .../request_with_1_modification_increment.xml      |  26 ++++
 .../java/org/apache/directory/api/i18n/I18n.java   |   4 +
 .../apache/directory/api/i18n/errors.properties    |   4 +
 .../request/modify/AddModifyRequestAttribute.java  |   7 ++
 .../actions/request/modify/StoreOperationType.java |   4 +-
 .../api/ldap/codec/api/LdapCodecConstants.java     |   2 +
 .../ldap/codec/factory/ModifyRequestFactory.java   |  12 +-
 .../api/ldap/codec/modify/ModifyRequestTest.java   | 131 ++++++++++++++++++++-
 .../api/ldap/model/constants/SchemaConstants.java  |   3 +
 .../api/ldap/model/entry/DefaultModification.java  |  19 ++-
 .../ldap/model/entry/ModificationOperation.java    |  16 ++-
 .../directory/api/ldap/model/ldif/LdifEntry.java   |   3 +
 .../directory/api/ldap/model/ldif/LdifReader.java  |  25 ++++
 .../api/ldap/model/ldif/LdifRevertor.java          |  12 +-
 .../api/ldap/model/message/ModifyRequest.java      |  57 ++++++++-
 .../api/ldap/model/message/ModifyRequestImpl.java  |  54 ++++++++-
 .../api/ldap/model/schema/SchemaUtils.java         |  56 +++++++++
 .../api/ldap/model/ldif/LdifEntryTest.java         |  70 +++++++++++
 .../ldap/model/message/ModifyRequestImplTest.java  |  24 ++++
 22 files changed, 608 insertions(+), 14 deletions(-)

diff --git a/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/Dsmlv2Grammar.java b/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/Dsmlv2Grammar.java
index c1b714a..1e03788 100644
--- a/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/Dsmlv2Grammar.java
+++ b/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/Dsmlv2Grammar.java
@@ -956,6 +956,10 @@ public final class Dsmlv2Grammar extends AbstractGrammar implements Grammar
                 {
                     modifyRequest.setCurrentOperation( LdapCodecConstants.OPERATION_REPLACE );
                 }
+                else if ( "increment".equals( attributeValue ) )
+                {
+                    modifyRequest.setCurrentOperation( LdapCodecConstants.OPERATION_INCREMENT );
+                }
                 else
                 {
                     throw new XmlPullParserException( I18n.err( I18n.ERR_03040_UNKNOWN_OPERATION ), xpp, null );
diff --git a/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/ModifyRequestDsml.java b/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/ModifyRequestDsml.java
index 47e0c10..52a1d56 100644
--- a/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/ModifyRequestDsml.java
+++ b/dsml/parser/src/main/java/org/apache/directory/api/dsmlv2/request/ModifyRequestDsml.java
@@ -205,6 +205,10 @@ public class ModifyRequestDsml
             {
                 modElement.addAttribute( "operation", "delete" );
             }
+            else if ( operation == ModificationOperation.INCREMENT_ATTRIBUTE )
+            {
+                modElement.addAttribute( "operation", "increment" );
+            }
         }
 
         return element;
@@ -426,6 +430,54 @@ public class ModifyRequestDsml
      * {@inheritDoc}
      */
     @Override
+    public ModifyRequest increment( Attribute attributeName )
+    {
+        getDecorated().increment( attributeName );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( Attribute attributeName, int increment )
+    {
+        getDecorated().increment( attributeName, increment );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( String attr )
+    {
+        getDecorated().increment( attr );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( String attr, int increment )
+    {
+        getDecorated().increment( attr, increment );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public ModifyRequest setMessageId( int messageId )
     {
         super.setMessageId( messageId );
diff --git a/dsml/parser/src/test/java/org/apache/directory/api/dsmlv2/modifyRequest/ModifyRequestTest.java b/dsml/parser/src/test/java/org/apache/directory/api/dsmlv2/modifyRequest/ModifyRequestTest.java
index b2accf5..d940415 100644
--- a/dsml/parser/src/test/java/org/apache/directory/api/dsmlv2/modifyRequest/ModifyRequestTest.java
+++ b/dsml/parser/src/test/java/org/apache/directory/api/dsmlv2/modifyRequest/ModifyRequestTest.java
@@ -317,6 +317,43 @@ public class ModifyRequestTest extends AbstractTest
 
 
     /**
+     * Test parsing of a request with a Modification element and Increment operation
+     * @throws NamingException
+     */
+    @Test
+    public void testRequestWith1ModificationIncrement() throws LdapException
+    {
+        Dsmlv2Parser parser = null;
+        try
+        {
+            parser = newParser();
+
+            parser.setInput( ModifyRequestTest.class.getResource( "request_with_1_modification_increment.xml" ).openStream(),
+                "UTF-8" );
+
+            parser.parse();
+        }
+        catch ( Exception e )
+        {
+            fail( e.getMessage() );
+        }
+
+        ModifyRequest modifyRequest = ( ModifyRequest ) parser.getBatchRequest().getCurrentRequest();
+        Collection<Modification> modifications = modifyRequest.getModifications();
+        assertEquals( 1, modifications.size() );
+
+        Modification modification = modifications.iterator().next();
+
+        assertEquals( ModificationOperation.INCREMENT_ATTRIBUTE, modification.getOperation() );
+
+        Attribute attribute = modification.getAttribute();
+
+        assertEquals( "uidnumber", attribute.getId() );
+        assertEquals( "CN=John Smith, DC=microsoft, DC=com", attribute.get().getString() );
+    }
+
+
+    /**
      * Test parsing of a request with a Modification element with Base64 Value
      * @throws NamingException
      */
diff --git a/dsml/parser/src/test/resources/org/apache/directory/api/dsmlv2/modifyRequest/request_with_1_modification_increment.xml b/dsml/parser/src/test/resources/org/apache/directory/api/dsmlv2/modifyRequest/request_with_1_modification_increment.xml
new file mode 100644
index 0000000..c1a214e
--- /dev/null
+++ b/dsml/parser/src/test/resources/org/apache/directory/api/dsmlv2/modifyRequest/request_with_1_modification_increment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  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.
+-->
+<batchRequest xmlns="urn:oasis:names:tc:DSML:2.0:core">
+	<modifyRequest dn="CN=Bob Rush,OU=Dev,DC=Example,DC=COM">
+		<modification name="uidNumber" operation="increment">
+			<value>CN=John Smith, DC=microsoft, DC=com</value>
+		</modification>
+	</modifyRequest>
+</batchRequest>
\ No newline at end of file
diff --git a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
index a8c51c3..05d5bc5 100644
--- a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
+++ b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
@@ -854,6 +854,10 @@ public enum I18n
     ERR_13863_MR_DOES_NOT_HAVE_A_COMP( "ERR_13863_MR_DOES_NOT_HAVE_A_COMP" ),
     ERR_13864_AT_DOES_NOT_HAVE_A_SUPERIOR_NOR_SYNTAX( "ERR_13864_AT_DOES_NOT_HAVE_A_SUPERIOR_NOR_SYNTAX" ),
     ERR_13865_ERROR_PARSING_AT( "ERR_13865_ERROR_PARSING_AT" ),
+    ERR_13866_MOD_INCREMENT_INVALID_VALUE( "ERR_13866_MOD_INCREMENT_INVALID_VALUE" ),
+    ERR_13867_MOD_INCREMENT_NO_ATTRIBUTE( "ERR_13867_MOD_INCREMENT_NO_ATTRIBUTE" ),
+    ERR_13868_MOD_INCREMENT_NO_INT_ATTRIBUTE( "ERR_13868_MOD_INCREMENT_NO_INT_ATTRIBUTE" ),
+    ERR_13869_MOD_INCREMENT_OVERFLOW( "ERR_13869_MOD_INCREMENT_OVERFLOW" ),
 
     // api-ldap-model subtree          13900-13999
     ERR_13900_INTEGER_TOKEN_NOT_INTEGER( "ERR_13900_INTEGER_TOKEN_NOT_INTEGER" ),
diff --git a/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties b/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
index 5bfb687..8802ed1 100644
--- a/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
+++ b/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
@@ -847,6 +847,10 @@ ERR_13862_SC_PARSING_FAILURE=Parser failure on syntax checker description:\n\t{0
 ERR_13863_MR_DOES_NOT_HAVE_A_COMP=The MatchingRule {0} does not have a comparator. This is invalid
 ERR_13864_AT_DOES_NOT_HAVE_A_SUPERIOR_NOR_SYNTAX=The AttributeType {0} does not have a superior nor a Syntax. This is invalid
 ERR_13865_ERROR_PARSING_AT=Error parsing attribute type {0}: {1}
+ERR_13866_MOD_INCREMENT_INVALID_VALUE=Increment operation on {0} with invalid increment {1}
+ERR_13867_MOD_INCREMENT_NO_ATTRIBUTE=Increment operation on a non existing attribute {0}
+ERR_13868_MOD_INCREMENT_NO_INT_ATTRIBUTE=Increment operation on a non integer attribute {0}
+ERR_13869_MOD_INCREMENT_OVERFLOW=Increment operation overflow for attribute {0}, value is {1}
 
 # api-ldap-model subtree          13900-13999
 ERR_13900_INTEGER_TOKEN_NOT_INTEGER=Value of INTEGER token {0} cannot be converted to an Integer
diff --git a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/AddModifyRequestAttribute.java b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/AddModifyRequestAttribute.java
index f9b4f92..a5f54e8 100644
--- a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/AddModifyRequestAttribute.java
+++ b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/AddModifyRequestAttribute.java
@@ -28,6 +28,7 @@ import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
 import org.apache.directory.api.ldap.codec.api.ResponseCarryingException;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.message.ModifyRequest;
 import org.apache.directory.api.ldap.model.message.ModifyResponseImpl;
 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
@@ -95,6 +96,12 @@ public class AddModifyRequestAttribute extends GrammarAction<LdapMessageContaine
             container.getCurrentModification().setAttribute( currentAttribute );
         }
 
+        // We can have an END transition if the operation was INCREMENT
+        if ( container.getCurrentModification().getOperation() == ModificationOperation.INCREMENT_ATTRIBUTE )
+        {
+            container.setGrammarEndAllowed( true );
+        }
+
         if ( LOG.isDebugEnabled() )
         {
             LOG.debug( I18n.msg( I18n.MSG_05128_MODIFYING_TYPE, type ) );
diff --git a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/StoreOperationType.java b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/StoreOperationType.java
index 23bb812..d6fe9bf 100644
--- a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/StoreOperationType.java
+++ b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/actions/request/modify/StoreOperationType.java
@@ -30,6 +30,7 @@ import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
 import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
 import org.apache.directory.api.ldap.model.entry.DefaultModification;
 import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.message.ModifyRequest;
 import org.apache.directory.api.util.Strings;
 import org.slf4j.Logger;
@@ -76,7 +77,8 @@ public class StoreOperationType extends GrammarAction<LdapMessageContainer<Modif
         try
         {
             // Store the current operation.
-            operation = IntegerDecoder.parse( tlv.getValue(), 0, 2 );
+            operation = IntegerDecoder.parse( tlv.getValue(), ModificationOperation.ADD_ATTRIBUTE.getValue(), 
+                ModificationOperation.INCREMENT_ATTRIBUTE.getValue() );
             Modification modification = new DefaultModification();
             modification.setOperation( operation );
             modifyRequest.addModification( modification );
diff --git a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/api/LdapCodecConstants.java b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/api/LdapCodecConstants.java
index fd53907..9af0f39 100644
--- a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/api/LdapCodecConstants.java
+++ b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/api/LdapCodecConstants.java
@@ -50,6 +50,8 @@ public final class LdapCodecConstants
 
     public static final int OPERATION_REPLACE = 2;
 
+    public static final int OPERATION_INCREMENT = 3;
+
     /** The filters */
     public static final int EQUALITY_MATCH_FILTER = 0;
 
diff --git a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/factory/ModifyRequestFactory.java b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/factory/ModifyRequestFactory.java
index d2f197f..7d044d7 100644
--- a/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/factory/ModifyRequestFactory.java
+++ b/ldap/codec/core/src/main/java/org/apache/directory/api/ldap/codec/factory/ModifyRequestFactory.java
@@ -28,6 +28,7 @@ import org.apache.directory.api.ldap.codec.api.LdapApiService;
 import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.message.Message;
 import org.apache.directory.api.ldap.model.message.ModifyRequest;
@@ -114,10 +115,15 @@ public final class ModifyRequestFactory implements Messagefactory
             if ( modification.getAttribute().size() != 0 )
             {
                 encodeValues( buffer, modification.getAttribute().iterator() );
-            }
 
-            // the value set
-            BerValue.encodeSet( buffer, start );
+                // the value set
+                BerValue.encodeSet( buffer, start );
+            }
+            else if ( modification.getOperation() != ModificationOperation.INCREMENT_ATTRIBUTE )
+            {
+                // the value set, if not a INCREMENT operation
+                BerValue.encodeSet( buffer, start );
+            }
 
             // The attribute type
             BerValue.encodeOctetString( buffer, attribute.getUpId() );
diff --git a/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/modify/ModifyRequestTest.java b/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/modify/ModifyRequestTest.java
index 6c4e8e3..e422da2 100644
--- a/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/modify/ModifyRequestTest.java
+++ b/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/modify/ModifyRequestTest.java
@@ -39,6 +39,7 @@ import org.apache.directory.api.ldap.codec.api.ResponseCarryingException;
 import org.apache.directory.api.ldap.codec.osgi.AbstractCodecServiceTest;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 import org.apache.directory.api.ldap.model.message.Control;
 import org.apache.directory.api.ldap.model.message.Message;
@@ -837,7 +838,7 @@ public class ModifyRequestTest extends AbstractCodecServiceTest
                 0x30, 0x33,             // LdapMessage
                   0x02, 0x01, 0x31,     // messageID MessageID
                   0x66, 0x2E,           // ModifyRequest
-                    0x04, 0x20,             // entry LDAPDN,
+                    0x04, 0x20,         // entry LDAPDN,
                       'c', 'n', '=', 't', 'e', 's', 't', 'M', 'o', 'd', 'i', 'f', 'y',
                       ',', 'o', 'u', '=', 'u', 's', 'e', 'r', 's', ',',
                       'o', 'u', '=', 's', 'y', 's', 't', 'e', 'm',
@@ -1061,4 +1062,132 @@ public class ModifyRequestTest extends AbstractCodecServiceTest
 
         assertArrayEquals( stream.array(), buffer.getBytes().array() );
     }
+
+
+    /**
+     * Test the decoding of a ModifyRequest with an add operation, and a
+     * modification with a type and an empty vals
+     */
+    @Test
+    public void testDecodeModifyRequestAddOperationModificationIncrement()
+        throws DecoderException, EncoderException
+    {
+        ByteBuffer stream = ByteBuffer.allocate( 0x3D );
+
+        stream.put( new byte[]
+            {
+                0x30, 0x3B,             // LdapMessage
+                  0x02, 0x01, 0x31,     // messageID MessageID
+                  0x66, 0x36,           // ModifyRequest
+                    0x04, 0x20,         // entry LDAPDN,
+                      'c', 'n', '=', 't', 'e', 's', 't', 'M', 'o', 'd', 'i', 'f', 'y',
+                      ',', 'o', 'u', '=', 'u', 's', 'e', 'r', 's', ',',
+                      'o', 'u', '=', 's', 'y', 's', 't', 'e', 'm',
+                    0x30, 0x012,
+                      0x30, 0x10,
+                        0x0A, 0x01, 0x03,
+                        0x30, 0x0B,
+                           0x04, 0x09,
+                            'u', 'i', 'd', 'n', 'u', 'm', 'b', 'e', 'r',
+            } );
+
+        stream.flip();
+
+        // Allocate a LdapMessage Container
+        LdapMessageContainer<ModifyRequest> ldapMessageContainer = new LdapMessageContainer<>( codec );
+
+        // Decode a ModifyRequest PDU
+        Asn1Decoder.decode( stream, ldapMessageContainer );
+
+        // Check the decoded PDU
+        ModifyRequest modifyRequest = ldapMessageContainer.getMessage();
+
+        assertEquals( 49, modifyRequest.getMessageId() );
+        assertEquals( "cn=testModify,ou=users,ou=system", modifyRequest.getName().toString() );
+
+        Object[] modifications = modifyRequest.getModifications().toArray();
+
+        assertEquals( 1, modifications.length );
+
+        Modification modification = ( Modification ) modifications[0];
+        
+        assertEquals( ModificationOperation.INCREMENT_ATTRIBUTE, modification.getOperation() );
+        Attribute attributeValue = modification.getAttribute();
+
+        assertEquals( "uidnumber", Strings.toLowerCaseAscii( attributeValue.getUpId() ) );
+        assertEquals( 0, attributeValue.size() );
+
+        // Check encode reverse
+        Asn1Buffer buffer = new Asn1Buffer();
+
+        LdapEncoder.encodeMessage( buffer, codec, modifyRequest );
+
+        assertArrayEquals( stream.array(), buffer.getBytes().array() );
+    }
+
+
+    /**
+     * Test the decoding of a ModifyRequest with an add operation, and a
+     * modification with a type and an empty vals
+     */
+    @Test
+    public void testDecodeModifyRequestAddOperationModificationIncrementWithValue()
+        throws DecoderException, EncoderException
+    {
+        ByteBuffer stream = ByteBuffer.allocate( 0x42 );
+
+        stream.put( new byte[]
+            {
+                0x30, 0x40,             // LdapMessage
+                  0x02, 0x01, 0x31,     // messageID MessageID
+                  0x66, 0x3B,           // ModifyRequest
+                    0x04, 0x20,         // entry LDAPDN,
+                      'c', 'n', '=', 't', 'e', 's', 't', 'M', 'o', 'd', 'i', 'f', 'y',
+                      ',', 'o', 'u', '=', 'u', 's', 'e', 'r', 's', ',',
+                      'o', 'u', '=', 's', 'y', 's', 't', 'e', 'm',
+                    0x30, 0x017,
+                      0x30, 0x15,
+                        0x0A, 0x01, 0x03,
+                        0x30, 0x10,
+                           0x04, 0x09,
+                            'u', 'i', 'd', 'n', 'u', 'm', 'b', 'e', 'r',
+                           0x31, 0x03,
+                             0x04, 0x01,
+                               '3'
+            } );
+
+        stream.flip();
+
+        // Allocate a LdapMessage Container
+        LdapMessageContainer<ModifyRequest> ldapMessageContainer = new LdapMessageContainer<>( codec );
+
+        // Decode a ModifyRequest PDU
+        Asn1Decoder.decode( stream, ldapMessageContainer );
+
+        // Check the decoded PDU
+        ModifyRequest modifyRequest = ldapMessageContainer.getMessage();
+
+        assertEquals( 49, modifyRequest.getMessageId() );
+        assertEquals( "cn=testModify,ou=users,ou=system", modifyRequest.getName().toString() );
+
+        Object[] modifications = modifyRequest.getModifications().toArray();
+
+        assertEquals( 1, modifications.length );
+
+        Modification modification = ( Modification ) modifications[0];
+        
+        assertEquals( ModificationOperation.INCREMENT_ATTRIBUTE, modification.getOperation() );
+        Attribute attributeValue = modification.getAttribute();
+
+        assertEquals( "uidnumber", Strings.toLowerCaseAscii( attributeValue.getUpId() ) );
+        assertEquals( 1, attributeValue.size() );
+        assertEquals( "3", attributeValue.get().getString() );
+
+        // Check encode reverse
+        Asn1Buffer buffer = new Asn1Buffer();
+
+        LdapEncoder.encodeMessage( buffer, codec, modifyRequest );
+
+        assertArrayEquals( stream.array(), buffer.getBytes().array() );
+    }
 }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/constants/SchemaConstants.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/constants/SchemaConstants.java
index dc2b81e..2133dee 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/constants/SchemaConstants.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/constants/SchemaConstants.java
@@ -2188,6 +2188,9 @@ public final class SchemaConstants
 
     // ---- Features ----------------------------------------------------------
     public static final String FEATURE_ALL_OPERATIONAL_ATTRIBUTES = "1.3.6.1.4.1.4203.1.5.1";
+    
+    // RFC 4525
+    public static final String FEATURE_MODIFY_INCREMENT = "1.3.6.1.1.14";
 
     // ----Administrative roles -----------------------------------------------
     // AutonomousArea
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/DefaultModification.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/DefaultModification.java
index 4ccca0e..96f0fe2 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/DefaultModification.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/DefaultModification.java
@@ -425,9 +425,22 @@ public class DefaultModification implements Modification
 
         sb.append( "Modification: " ).
             append( operation ).
-            append( "\n" ).
-            append( ", attribute : " ).
-            append( attribute );
+            append( ", [" ).
+            append( attribute.getId() );
+        
+        if ( attribute.size() == 0 )
+        {
+            if ( operation == ModificationOperation.INCREMENT_ATTRIBUTE )
+            { 
+                sb.append( " : 1" );
+            }
+        }
+        else
+        {
+            sb.append( " : " ).append( attribute );
+        }
+        
+        sb.append( "]" );
 
         return sb.toString();
     }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/ModificationOperation.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/ModificationOperation.java
index bbb1af7..b51f424 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/ModificationOperation.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/ModificationOperation.java
@@ -23,7 +23,9 @@ package org.apache.directory.api.ldap.model.entry;
 /**
  * An enum storing the different modification operation which can be used
  * in a Modification. There is a one to one mapping with the DirContext.ADD_ATTRIBUTE,
- * DirContext.REMOVE_ATTRIBUTE, DirContext.REPLACE_ATTRIBUTE
+ * DirContext.REMOVE_ATTRIBUTE, DirContext.REPLACE_ATTRIBUTE.
+ * 
+ * We have added the INCREMENT operation (RFC 4525)
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
@@ -36,7 +38,10 @@ public enum ModificationOperation
     REMOVE_ATTRIBUTE(1),
     
     /** Replaced attribute value */
-    REPLACE_ATTRIBUTE(2);
+    REPLACE_ATTRIBUTE(2),
+    
+    /** Increment operation, RFC 4525 */
+    INCREMENT_ATTRIBUTE(3);
 
     /** Internal value */
     private int value;
@@ -83,6 +88,10 @@ public enum ModificationOperation
         {
             return REPLACE_ATTRIBUTE;
         }
+        else if ( value == INCREMENT_ATTRIBUTE.value )
+        {
+            return INCREMENT_ATTRIBUTE;
+        }
         else
         {
             return null;
@@ -107,6 +116,9 @@ public enum ModificationOperation
             case REMOVE_ATTRIBUTE:
                 return "remove";
 
+            case INCREMENT_ATTRIBUTE:
+                return "increment";
+                
             default:
                 return "";
         }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifEntry.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifEntry.java
index 2c3d174..e80f569 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifEntry.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifEntry.java
@@ -406,6 +406,7 @@ public class LdifEntry implements Cloneable, Externalizable, Iterable<Attribute>
      * <li>ModificationOperation.ADD_ATTRIBUTE</li>
      * <li>ModificationOperation.REMOVE_ATTRIBUTE</li>
      * <li>ModificationOperation.REPLACE_ATTRIBUTE</li>
+     * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li>
      * </ul>
      * 
      * @param attr The attribute to be added
@@ -429,6 +430,7 @@ public class LdifEntry implements Cloneable, Externalizable, Iterable<Attribute>
      * <li>ModificationOperation.ADD_ATTRIBUTE</li>
      * <li>ModificationOperation.REMOVE_ATTRIBUTE</li>
      * <li>ModificationOperation.REPLACE_ATTRIBUTE</li>
+     * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li>
      * </ul>
      * 
      * @param id The attribute's ID
@@ -454,6 +456,7 @@ public class LdifEntry implements Cloneable, Externalizable, Iterable<Attribute>
      * <li>ModificationOperation.ADD_ATTRIBUTE</li>
      * <li>ModificationOperation.REMOVE_ATTRIBUTE</li>
      * <li>ModificationOperation.REPLACE_ATTRIBUTE</li>
+     * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li>
      * </ul>
      * 
      * @param id The attribute's ID
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifReader.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifReader.java
index 697cc7c..a18e203 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifReader.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifReader.java
@@ -1114,6 +1114,7 @@ public class LdifReader implements Iterable<LdifEntry>, Closeable
      * <pre>
      * &lt;changerecord&gt; ::= "changetype:" FILL "modify" SEP &lt;mod-spec&gt; &lt;mod-specs-e&gt;
      * &lt;mod-spec&gt; ::= "add:" &lt;mod-val&gt; | "delete:" &lt;mod-val-del&gt; | "replace:" &lt;mod-val&gt;
+     *                      | "increment:" &lt;mod-val&gt;
      * &lt;mod-specs-e&gt; ::= &lt;mod-spec&gt;
      * &lt;mod-specs-e&gt; | e
      * &lt;mod-val&gt; ::= FILL ATTRIBUTE-DESCRIPTION SEP ATTRVAL-SPEC &lt;attrval-specs-e&gt; "-" SEP
@@ -1227,6 +1228,30 @@ public class LdifReader implements Iterable<LdifEntry>, Closeable
 
                 state = ATTRVAL_SPEC_OR_SEP;
             }
+            else if ( lowerLine.startsWith( "increment:" ) )
+            {
+                if ( ( state != MOD_SPEC ) && ( state != ATTRVAL_SPEC ) )
+                {
+                    String msg = I18n.err( I18n.ERR_13414_BAD_MODIFY_SEPARATOR_2, lineNumber );
+                    LOG.error( msg );
+                    throw new LdapLdifException( msg );
+                }
+
+                modified = Strings.trim( line.substring( "increment:".length() ) );
+                modificationType = ModificationOperation.INCREMENT_ATTRIBUTE;
+                
+                if ( schemaManager != null )
+                {
+                    AttributeType attributeType = schemaManager.getAttributeType( modified );
+                    attribute = new DefaultAttribute( modified, attributeType );
+                }
+                else
+                {
+                    attribute = new DefaultAttribute( modified );
+                }
+
+                state = ATTRVAL_SPEC_OR_SEP;
+            }
             else
             {
                 if ( ( state != ATTRVAL_SPEC ) && ( state != ATTRVAL_SPEC_OR_SEP ) )
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
index 9c3b18f..22b03d8 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
@@ -221,7 +221,17 @@ public final class LdifRevertor
                         continue;
                     }
 
-                    reverseModification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, previous );
+                    reverseModification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, 
+                        previous );
+                    reverseModifications.add( 0, reverseModification );
+                    break;
+                    
+                case INCREMENT_ATTRIBUTE:
+                    mod = modification.getAttribute();
+                    previous = clonedEntry.get( mod.getId() );
+
+                    reverseModification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                        previous );
                     reverseModifications.add( 0, reverseModification );
                     break;
 
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequest.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequest.java
index ccbdf38..3209d13 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequest.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequest.java
@@ -46,7 +46,8 @@ import org.apache.directory.api.ldap.model.name.Dn;
  *                        operation       ENUMERATED {
  *                                                add     (0),
  *                                                delete  (1),
- *                                                replace (2) },
+ *                                                replace (2),
+ *                                                ... },
  *                        modification    AttributeTypeAndValues } }
  * 
  *        AttributeTypeAndValues ::= SEQUENCE {
@@ -93,6 +94,22 @@ import org.apache.directory.api.ldap.model.name.Dn;
  *  borrow good ideas and familiar signatures, interfaces and classes where we
  *  can.
  *  
+ *  RFC 4525 suggest to add an operation in the enumeration:
+ *  
+ * <pre>
+ *        ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ *                object          LDAPDN,
+ *                modification    SEQUENCE OF SEQUENCE {
+ * 
+ *                        operation       ENUMERATED {
+ *                                                add       (0),
+ *                                                delete    (1),
+ *                                                replace   (2),
+ *                                                increment (3),
+ *                                                ... },
+ * </pre>
+ * 
+ *  
  *  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * 
  */
@@ -267,6 +284,44 @@ public interface ModifyRequest extends SingleReplyRequest, AbandonableRequest
 
 
     /**
+     * marks a given attribute for increment by 1 in the target entry.
+     *
+     * @param attributeName the attribute to be incremented
+     * @return The ModifyRequest instance
+     */
+    ModifyRequest increment( String attributeName );
+
+
+    /**
+     * marks a given attribute for increment in the target entry.
+     *
+     * @param attributeName the attribute to be incremented
+     * @param increment The increment value (&gt;=1) 
+     * @return The ModifyRequest instance
+     */
+    ModifyRequest increment( String attributeName, int increment );
+
+
+    /**
+     * marks a given attribute for increment by 1 in the target entry.
+     *
+     * @param attr the attribute to be incremented
+     * @return The ModifyRequest instance
+     */
+    ModifyRequest increment( Attribute attr );
+
+
+    /**
+     * marks a given attribute for increment in the target entry.
+     *
+     * @param attr the attribute to be incremented
+     * @param increment The increment value (&gt;=1) 
+     * @return The ModifyRequest instance
+     */
+    ModifyRequest increment( Attribute attr,  int increment );
+
+
+    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequestImpl.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequestImpl.java
index a9cd293..c961fda 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequestImpl.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/message/ModifyRequestImpl.java
@@ -274,9 +274,59 @@ public class ModifyRequestImpl extends AbstractAbandonableRequest implements Mod
      * {@inheritDoc}
      */
     @Override
-    public ModifyRequest remove( String attributerName )
+    public ModifyRequest remove( String attributeName )
     {
-        addModification( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attributerName ) );
+        addModification( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attributeName ) );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( String attributeName )
+    {
+        addModification( new DefaultModification( ModificationOperation.INCREMENT_ATTRIBUTE, attributeName ) );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( String attributeName, int increment )
+    {
+        addModification( new DefaultModification( ModificationOperation.INCREMENT_ATTRIBUTE, attributeName, 
+            Integer.toString( increment ) ) );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( Attribute attr )
+    {
+        addModification( attr, ModificationOperation.INCREMENT_ATTRIBUTE );
+
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ModifyRequest increment( Attribute attr, int increment )
+    {
+        addModification( new DefaultModification( ModificationOperation.INCREMENT_ATTRIBUTE, attr.getId(), 
+            Integer.toString( increment ) ) );
 
         return this;
     }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/schema/SchemaUtils.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/schema/SchemaUtils.java
index c668cda..721078d 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/schema/SchemaUtils.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/schema/SchemaUtils.java
@@ -28,6 +28,7 @@ import java.util.UUID;
 
 import org.apache.directory.api.i18n.I18n;
 import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.Entry;
 import org.apache.directory.api.ldap.model.entry.Modification;
@@ -117,6 +118,61 @@ public final class SchemaUtils
                     }
 
                     break;
+                    
+                case INCREMENT_ATTRIBUTE:
+                    // The incremented attribute might not exist
+                    AttributeType attributeType = mod.getAttribute().getAttributeType();
+                    String incrementStr = mod.getAttribute().getString();
+                    int increment = 1;
+                    
+                    if ( !Strings.isEmpty( incrementStr ) )
+                    {
+                        try
+                        { 
+                            increment = Integer.parseInt( incrementStr );
+                        }
+                        catch ( NumberFormatException nfe )
+                        {
+                            throw new IllegalArgumentException( I18n.err( I18n.ERR_13866_MOD_INCREMENT_INVALID_VALUE,  
+                                attributeType.getName(), incrementStr ) );
+                        }
+                    }
+                    Attribute modified = targetEntry.get( attributeType );
+
+                    if ( !targetEntry.containsAttribute( attributeType ) )
+                    {
+                        throw new IllegalArgumentException( I18n.err( I18n.ERR_13867_MOD_INCREMENT_NO_ATTRIBUTE,  
+                            attributeType.getName() ) );
+                    }
+
+                    if ( !SchemaConstants.INTEGER_SYNTAX.equals( modified.getAttributeType().getSyntax().getOid() ) )
+                    {
+                        throw new IllegalArgumentException( I18n.err( I18n.ERR_13868_MOD_INCREMENT_NO_INT_ATTRIBUTE,  
+                            attributeType.getName() ) );
+                    }
+                    else
+                    {
+                        Value[] newValues = new Value[ modified.size() ];
+                        int i = 0;
+                        
+                        for ( Value value : modified )
+                        {
+                            int intValue = Integer.parseInt( value.getNormalized() );
+                            
+                            if ( intValue == Integer.MAX_VALUE )
+                            {
+                                throw new IllegalArgumentException( I18n.err( I18n.ERR_13869_MOD_INCREMENT_OVERFLOW,  
+                                    attributeType.getName(), intValue ) );
+                            }
+                            
+                            newValues[i++] = new Value( Integer.toString( intValue + increment ) );
+                            modified.remove( value );
+                        }
+                        
+                        modified.add( newValues );
+                    }
+                    
+                    break;
 
                 default:
                     throw new IllegalStateException( I18n.err( I18n.ERR_13775_UNDEFINED_MODIFICATION_TYPE, mod.getOperation() ) );
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifEntryTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifEntryTest.java
index c00b3cf..270363c 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifEntryTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifEntryTest.java
@@ -564,6 +564,76 @@ public class LdifEntryTest
 
 
     /**
+     * Test a Modify changeType LdifEntry with increment operation
+     */
+    @Test
+    public void testLdifEntryChangeTypeModifyIncrement() throws Exception
+    {
+        String ldif =
+            "changetype: modify\n" +
+                "increment: uidNumber\n" +
+                "-";
+
+        LdifEntry ldifEntry = new LdifEntry( "cn=app1,ou=applications,ou=conf,dc=apache,dc=org", ldif );
+
+        assertNotNull( ldifEntry );
+        assertEquals( ChangeType.Modify, ldifEntry.getChangeType() );
+        assertNull( ldifEntry.getEntry() );
+        assertEquals( "cn=app1,ou=applications,ou=conf,dc=apache,dc=org", ldifEntry.getDn().getName() );
+        assertFalse( ldifEntry.hasControls() );
+        assertTrue( ldifEntry.isLdifChange() );
+
+        // Check the modification
+        assertNotNull( ldifEntry.getModifications() );
+
+        for ( Modification modification : ldifEntry.getModifications() )
+        {
+            assertEquals( ModificationOperation.INCREMENT_ATTRIBUTE, modification.getOperation() );
+            Attribute attribute = modification.getAttribute();
+
+            assertNotNull( attribute );
+            assertEquals( "uidnumber", attribute.getId() );
+        }
+    }
+
+
+    /**
+     * Test a Modify changeType LdifEntry with increment operation
+     */
+    @Test
+    public void testLdifEntryChangeTypeModifyIncrementNumber() throws Exception
+    {
+        String ldif =
+            "changetype: modify\n" +
+                "increment: uidNumber\n" +
+                "uidNumber: 3\n" +
+                "-";
+
+        LdifEntry ldifEntry = new LdifEntry( "cn=app1,ou=applications,ou=conf,dc=apache,dc=org", ldif );
+
+        assertNotNull( ldifEntry );
+        assertEquals( ChangeType.Modify, ldifEntry.getChangeType() );
+        assertNull( ldifEntry.getEntry() );
+        assertEquals( "cn=app1,ou=applications,ou=conf,dc=apache,dc=org", ldifEntry.getDn().getName() );
+        assertFalse( ldifEntry.hasControls() );
+        assertTrue( ldifEntry.isLdifChange() );
+
+        // Check the modification
+        assertNotNull( ldifEntry.getModifications() );
+
+        for ( Modification modification : ldifEntry.getModifications() )
+        {
+            assertEquals( ModificationOperation.INCREMENT_ATTRIBUTE, modification.getOperation() );
+            Attribute attribute = modification.getAttribute();
+
+            assertNotNull( attribute );
+            assertEquals( "uidnumber", attribute.getId() );
+            assertEquals( "3", attribute.getString() );
+        }
+    }
+
+
+    /**
      * Test a Modify changeType LdifEntry with no operation
      */
     @Test(expected = LdapLdifException.class)
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/message/ModifyRequestImplTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/message/ModifyRequestImplTest.java
index 3a4446f..858b832 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/message/ModifyRequestImplTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/message/ModifyRequestImplTest.java
@@ -527,6 +527,30 @@ public class ModifyRequestImplTest
             {
                 return this;
             }
+            
+            
+            public ModifyRequest increment( Attribute attr )
+            {
+                return this;
+            }
+            
+            
+            public ModifyRequest increment( Attribute attr, int increment )
+            {
+                return this;
+            }
+            
+            
+            public ModifyRequest increment( String attributerName )
+            {
+                return this;
+            }
+            
+            
+            public ModifyRequest increment( String attributerName, int increment )
+            {
+                return this;
+            }
         };
 
         ModifyRequestImpl req1 = getRequest();