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 2006/01/04 01:26:18 UTC

svn commit: r365779 - in /directory/trunk/ldap-common/src: main/java/org/apache/ldap/common/asn1/pojo/psearch/ test/java/org/apache/ldap/asn1/ test/java/org/apache/ldap/asn1/pojo/ test/java/org/apache/ldap/asn1/pojo/psearch/

Author: akarasulu
Date: Tue Jan  3 16:26:11 2006
New Revision: 365779

URL: http://svn.apache.org/viewcvs?rev=365779&view=rev
Log:
codec for persistent search control

Added:
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java   (with props)
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java   (with props)
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java   (with props)
    directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/
    directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/
    directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/
    directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/PSearchControlTest.java
Modified:
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControl.java

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControl.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControl.java?rev=365779&r1=365778&r2=365779&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControl.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControl.java Tue Jan  3 16:26:11 2006
@@ -7,7 +7,6 @@
 import org.apache.asn1.ber.tlv.UniversalTag;
 import org.apache.asn1.ber.tlv.Value;
 import org.apache.asn1.codec.EncoderException;
-import org.apache.ldap.common.util.StringTools;
 
 
 public class PSearchControl extends Asn1Object

Added: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java?rev=365779&view=auto
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java (added)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java Tue Jan  3 16:26:11 2006
@@ -0,0 +1,80 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.common.asn1.pojo.psearch;
+
+
+import org.apache.asn1.ber.AbstractContainer;
+import org.apache.asn1.ber.IAsn1Container;
+import org.apache.asn1.ber.grammar.IGrammar;
+
+
+/**
+ *  
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PSearchControlContainer extends AbstractContainer implements IAsn1Container 
+{
+    /** PSearchControl */
+    private PSearchControl control;
+
+    /**
+     * Creates a new PSearchControlContainer object.
+     * We will store one grammar, it's enough ...
+     */
+    public PSearchControlContainer()
+    {
+        super( );
+        currentGrammar = 0;
+        grammars = new IGrammar[PSearchControlStatesEnum.NB_GRAMMARS];
+        grammarStack = new IGrammar[1];
+        stateStack = new int[1];
+        nbGrammars = 0;
+
+        grammars[PSearchControlStatesEnum.PSEARCH_GRAMMAR] = PSearchControlGrammar.getInstance();
+        grammarStack[currentGrammar] = grammars[PSearchControlStatesEnum.PSEARCH_GRAMMAR];
+        states = PSearchControlStatesEnum.getInstance();
+    }
+
+
+    /**
+     * @return Returns the persistent search control.
+     */
+    public PSearchControl getPSearchControl()
+    {
+
+        return control;
+    }
+
+    
+    /**
+     * Set a PSearchControl Object into the container. It will be completed
+     * by the ldapDecoder.
+     *
+     * @param control the PSearchControl to set.
+     */
+    public void setPSearchControl( PSearchControl control )
+    {
+        this.control = control;
+    }
+
+    
+    public void clean()
+    {
+        super.clean();
+        control = null;
+    }
+}

Propchange: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java?rev=365779&view=auto
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java (added)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java Tue Jan  3 16:26:11 2006
@@ -0,0 +1,195 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.common.asn1.pojo.psearch;
+
+import org.apache.asn1.ber.IAsn1Container;
+import org.apache.asn1.ber.grammar.AbstractGrammar;
+import org.apache.asn1.ber.grammar.GrammarAction;
+import org.apache.asn1.ber.grammar.GrammarTransition;
+import org.apache.asn1.ber.grammar.IGrammar;
+import org.apache.asn1.ber.tlv.UniversalTag;
+import org.apache.asn1.ber.tlv.Value;
+import org.apache.asn1.codec.DecoderException;
+import org.apache.asn1.util.BooleanDecoder;
+import org.apache.asn1.util.BooleanDecoderException;
+import org.apache.asn1.util.IntegerDecoder;
+import org.apache.asn1.util.IntegerDecoderException;
+import org.apache.ldap.common.asn1.codec.grammar.LdapStatesEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * This class implements the PSearchControl. All the actions are declared in this
+ * class. As it is a singleton, these declaration are only done once.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PSearchControlGrammar extends AbstractGrammar implements IGrammar
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( PSearchControlGrammar.class );
+
+    /** The instance of grammar. PSearchControlGrammar is a singleton */
+    private static IGrammar instance = new PSearchControlGrammar();
+
+    /**
+     * Creates a new ModifyDNRequestGrammar object.
+     */
+    private PSearchControlGrammar()
+    {
+        name = PSearchControlGrammar.class.getName();
+        statesEnum = PSearchControlStatesEnum.getInstance();
+
+        // Create the transitions table
+        super.transitions = new GrammarTransition[PSearchControlStatesEnum.LAST_PSEARCH_STATE][256];
+
+        super.transitions[PSearchControlStatesEnum.PSEARCH_SEQUENCE_TAG][UniversalTag.SEQUENCE_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.PSEARCH_SEQUENCE_TAG,
+                PSearchControlStatesEnum.PSEARCH_SEQUENCE_VALUE, null );
+
+        super.transitions[PSearchControlStatesEnum.PSEARCH_SEQUENCE_VALUE][UniversalTag.SEQUENCE_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.PSEARCH_SEQUENCE_VALUE,
+                PSearchControlStatesEnum.CHANGE_TYPES_TAG, 
+                new GrammarAction( "Init PSearchControl" )
+                {
+                    public void action( IAsn1Container container ) 
+                    {
+                        PSearchControlContainer psearchContainer = ( PSearchControlContainer ) container;
+                        PSearchControl control = new PSearchControl();
+                        psearchContainer.setPSearchControl( control );
+                    }
+                }
+            );
+
+        super.transitions[PSearchControlStatesEnum.CHANGE_TYPES_TAG][UniversalTag.INTEGER_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.CHANGE_TYPES_TAG,
+                PSearchControlStatesEnum.CHANGE_TYPES_VALUE, null );
+
+        super.transitions[PSearchControlStatesEnum.CHANGE_TYPES_VALUE][UniversalTag.INTEGER_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.CHANGE_TYPES_VALUE, 
+                PSearchControlStatesEnum.CHANGES_ONLY_TAG, 
+                new GrammarAction( "Set PSearchControl changeTypes" )
+                {
+                    public void action( IAsn1Container container ) throws DecoderException
+                    {
+                        PSearchControlContainer psearchContainer = ( PSearchControlContainer ) container;
+                        Value value = psearchContainer.getCurrentTLV().getValue();
+                        
+                        try
+                        {
+                            int changeTypes = IntegerDecoder.parse( value );
+                            
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "changeTypes = " + changeTypes );
+                            }
+                            
+                            psearchContainer.getPSearchControl().setChangeTypes( changeTypes );
+                        }
+                        catch ( IntegerDecoderException e )
+                        {
+                            String msg = "failed to decode the changeTypes for PSearchControl";
+                            log.error( msg, e );
+                            throw new DecoderException( msg );
+                        }
+                    }
+                }
+            );
+
+        super.transitions[PSearchControlStatesEnum.CHANGES_ONLY_TAG][UniversalTag.BOOLEAN_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.CHANGES_ONLY_TAG,
+                PSearchControlStatesEnum.CHANGES_ONLY_VALUE, null );
+
+        super.transitions[PSearchControlStatesEnum.CHANGES_ONLY_VALUE][UniversalTag.BOOLEAN_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.CHANGES_ONLY_VALUE, 
+                PSearchControlStatesEnum.RETURN_ECS_TAG, 
+                new GrammarAction( "Set PSearchControl changesOnly" )
+                {
+                    public void action( IAsn1Container container ) throws DecoderException
+                    {
+                        PSearchControlContainer psearchContainer = ( PSearchControlContainer ) container;
+                        Value value = psearchContainer.getCurrentTLV().getValue();
+                        
+                        try
+                        {
+                            boolean changesOnly = BooleanDecoder.parse( value );
+                            
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "changesOnly = " + changesOnly );
+                            }
+                            
+                            psearchContainer.getPSearchControl().setChangesOnly( changesOnly );
+                        }
+                        catch ( BooleanDecoderException e )
+                        {
+                            String msg = "failed to decode the changesOnly for PSearchControl";
+                            log.error( msg, e );
+                            throw new DecoderException( msg );
+                        }
+                    }
+                }
+            );
+
+        super.transitions[PSearchControlStatesEnum.RETURN_ECS_TAG][UniversalTag.BOOLEAN_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.RETURN_ECS_TAG,
+                PSearchControlStatesEnum.RETURN_ECS_VALUE, null );
+
+        super.transitions[PSearchControlStatesEnum.RETURN_ECS_VALUE][UniversalTag.BOOLEAN_TAG] =
+            new GrammarTransition( PSearchControlStatesEnum.RETURN_ECS_VALUE, 
+                LdapStatesEnum.GRAMMAR_END, 
+                new GrammarAction( "Set PSearchControl returnECs" )
+                {
+                    public void action( IAsn1Container container ) throws DecoderException
+                    {
+                        PSearchControlContainer psearchContainer = ( PSearchControlContainer ) container;
+                        Value value = psearchContainer.getCurrentTLV().getValue();
+                        
+                        try
+                        {
+                            boolean returnECs = BooleanDecoder.parse( value );
+                            
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "returnECs = " + returnECs );
+                            }
+                            
+                            psearchContainer.getPSearchControl().setReturnECs( returnECs );
+                        }
+                        catch ( BooleanDecoderException e )
+                        {
+                            String msg = "failed to decode the returnECs for PSearchControl";
+                            log.error( msg, e );
+                            throw new DecoderException( msg );
+                        }
+                    }
+                }
+            );
+    }
+
+    /**
+     * This class is a singleton.
+     *
+     * @return An instance on this grammar
+     */
+    public static IGrammar getInstance()
+    {
+        return instance;
+    }
+}

Propchange: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlGrammar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java?rev=365779&view=auto
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java (added)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java Tue Jan  3 16:26:11 2006
@@ -0,0 +1,185 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.common.asn1.pojo.psearch;
+
+
+import org.apache.asn1.ber.grammar.IStates;
+import org.apache.asn1.ber.grammar.IGrammar;
+
+
+/**
+ * This class store the PSearchControl's grammar constants.
+ * It is also used for debugging purposes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PSearchControlStatesEnum implements IStates
+{
+    //~ Static fields/initializers -----------------------------------------------------------------
+
+    //=========================================================================
+    // Persistent search control grammar states 
+    //=========================================================================
+
+    /** Sequence Tag */
+    public static int PSEARCH_SEQUENCE_TAG = 0;
+
+    /** Sequence Value */
+    public static int PSEARCH_SEQUENCE_VALUE = 1;
+
+    /** changeTypes Tag */
+    public static int CHANGE_TYPES_TAG = 2;
+
+    /** changeTypes Value */
+    public static int CHANGE_TYPES_VALUE = 3;
+
+    /** changesOnly Tag */
+    public static int CHANGES_ONLY_TAG = 4;
+
+    /** changesOnly Value */
+    public static int CHANGES_ONLY_VALUE = 5;
+
+    /** returnECs Tag */
+    public static int RETURN_ECS_TAG = 6;
+
+    /** returnECs Value */
+    public static int RETURN_ECS_VALUE = 7;
+    
+    /** terminal state */
+    public static int LAST_PSEARCH_STATE = 8;
+
+    //=========================================================================
+    // Grammars declaration.
+    //=========================================================================
+    /** PSsearch grammar */
+    public static final int PSEARCH_GRAMMAR_SWITCH = 0x0100;
+
+    /** PSearch grammar number */
+    public static final int PSEARCH_GRAMMAR = 0;
+
+    /** The total number of grammars used */
+    public static final int NB_GRAMMARS = 1;
+
+    //=========================================================================
+    // Grammar switches debug strings 
+    //=========================================================================
+    /** A string representation of grammars */
+    private static String[] GrammarSwitchString =
+        new String[]
+        {
+            "PSEARCH_GRAMMAR_SWITCH"
+        };
+
+    //=========================================================================
+    // States debug strings 
+    //=========================================================================
+    /** A string representation of all the states */
+    private static String[] PSearchString = new String[]
+    {
+        "PSEARCH_SEQUENCE_TAG",
+        "PSEARCH_SEQUENCE_VALUE",
+        "CHANGE_TYPES_TAG",
+        "CHANGE_TYPES_VALUE",
+        "CHANGES_ONLY_TAG",
+        "CHANGES_ONLY_VALUE",
+        "RETURN_ECS_TAG",
+        "RETURN_ECS_VALUE"
+    };
+
+    /** The instance */
+    private static PSearchControlStatesEnum instance = new PSearchControlStatesEnum();
+
+    //~ Constructors -------------------------------------------------------------------------------
+
+    /**
+     * This is a private constructor. This class is a singleton
+     *
+     */
+    private PSearchControlStatesEnum()
+    {
+    }
+
+    //~ Methods ------------------------------------------------------------------------------------
+
+    /**
+     * Get an instance of this class
+     * @return An instance on this class
+     */
+    public static IStates getInstance()
+    {
+        return instance;
+    }
+
+    /**
+     * Get the grammar name
+     * @param grammar The grammar code
+     * @return The grammar name
+     */
+    public String getGrammarName( int grammar )
+    {
+        switch ( grammar )
+        {
+            case PSEARCH_GRAMMAR: return "PSEARCH_GRAMMAR";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Get the grammar name
+     * @param grammar The grammar class
+     * @return The grammar name
+     */
+    public String getGrammarName( IGrammar grammar )
+    {
+        if ( grammar instanceof PSearchControlGrammar )
+        {
+            return "PSEARCH_GRAMMAR";
+        }
+        
+        return "UNKNOWN GRAMMAR";
+    }
+
+    /**
+     * Get the string representing the state
+     * 
+     * @param grammar The current grammar being used
+     * @param state The state number
+     * @return The String representing the state
+     */
+    public String getState( int grammar, int state )
+    {
+
+        if ( ( state & GRAMMAR_SWITCH_MASK ) != 0 )
+        {
+            return ( state == END_STATE ) ? "END_STATE"
+                : GrammarSwitchString[( ( state & GRAMMAR_SWITCH_MASK ) >> 8 ) - 1];
+        }
+        else
+        {
+
+            switch ( grammar )
+            {
+
+                case PSEARCH_GRAMMAR :
+                    return ( ( state == GRAMMAR_END ) ? "PSEARCH_END_STATE" : PSearchString[state] );
+
+                default :
+                    return "UNKNOWN";
+            }
+        }
+    }
+}

Propchange: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/asn1/pojo/psearch/PSearchControlStatesEnum.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/PSearchControlTest.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/PSearchControlTest.java?rev=365779&view=auto
==============================================================================
--- directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/PSearchControlTest.java (added)
+++ directory/trunk/ldap-common/src/test/java/org/apache/ldap/asn1/pojo/psearch/PSearchControlTest.java Tue Jan  3 16:26:11 2006
@@ -0,0 +1,100 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.asn1.pojo.psearch;
+
+import java.nio.ByteBuffer;
+
+import javax.naming.NamingException;
+
+import org.apache.asn1.codec.DecoderException;
+import org.apache.asn1.ber.Asn1Decoder;
+import org.apache.ldap.common.asn1.codec.LdapDecoder;
+import org.apache.ldap.common.asn1.pojo.psearch.PSearchControl;
+import org.apache.ldap.common.asn1.pojo.psearch.PSearchControlContainer;
+import org.apache.ldap.common.util.StringTools;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Test the PSearchControlTest codec
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PSearchControlTest extends TestCase {
+    /**
+     * Test the decoding of a PSearchControl
+     */
+    public void testDecodeModifyDNRequestSuccess() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x0b );
+        bb.put( new byte[]
+            {
+                0x30, 0x09, 		// PersistentSearch ::= SEQUENCE {
+				0x02, 0x01, 0x01, 	//     changeTypes INTEGER,
+				0x01, 0x01, 0x00,	//     changesOnly BOOLEAN,
+                0x01, 0x01, 0x00    //     returnECs   BOOLEAN
+                                    // }
+            } );
+        bb.flip();
+
+        PSearchControlContainer container = new PSearchControlContainer();
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+            de.printStackTrace();
+            Assert.fail( de.getMessage() );
+        }
+        
+        PSearchControl control = container.getPSearchControl();
+        assertEquals( 1, control.getChangeTypes() );
+        assertEquals( false, control.isChangesOnly() );
+        assertEquals( false, control.isReturnECs() );
+    }
+
+    
+    /**
+     * Test encoding of a PSearchControl.
+     */
+    public void testEncodePSearchControl() throws Exception
+    {
+        ByteBuffer bb = ByteBuffer.allocate( 0x0b );
+        bb.put( new byte[]
+        {
+            0x30, 0x09,         // PersistentSearch ::= SEQUENCE {
+            0x02, 0x01, 0x01,   //     changeTypes INTEGER,
+            0x01, 0x01, 0x00,   //     changesOnly BOOLEAN,
+            0x01, 0x01, 0x00    //     returnECs   BOOLEAN
+                                // }
+        } );
+
+        String expected = StringTools.dumpBytes( bb.array() );
+        bb.flip();
+        
+        PSearchControl ctrl = new PSearchControl();
+        ctrl.setChangesOnly( false );
+        ctrl.setReturnECs( false );
+        ctrl.setChangeTypes( 1 );
+        bb = ctrl.encode( null );
+        String decoded = StringTools.dumpBytes( bb.array() );
+        assertEquals( expected, decoded );
+    }
+}