001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.codec.search.controls.entryChange;
021    
022    
023    import java.nio.ByteBuffer;
024    
025    import org.apache.directory.shared.asn1.AbstractAsn1Object;
026    import org.apache.directory.shared.asn1.ber.tlv.TLV;
027    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
028    import org.apache.directory.shared.asn1.ber.tlv.Value;
029    import org.apache.directory.shared.asn1.codec.EncoderException;
030    import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
031    import org.apache.directory.shared.ldap.name.LdapDN;
032    import org.apache.directory.shared.ldap.util.StringTools;
033    
034    
035    /**
036     * A response control that may be returned by Persistent Search entry responses.
037     * It contains addition change information to describe the exact change that
038     * occurred to an entry. The exact details of this control are covered in section
039     * 5 of this (yes) expired draft: <a
040     * href="http://www3.ietf.org/proceedings/01aug/I-D/draft-ietf-ldapext-psearch-03.txt">
041     * Persistent Search Draft v03</a> which is printed out below for convenience:
042     * 
043     * <pre>
044     *    5.  Entry Change Notification Control
045     *    
046     *    This control provides additional information about the change the caused
047     *    a particular entry to be returned as the result of a persistent search.
048     *    The controlType is &quot;2.16.840.1.113730.3.4.7&quot;.  If the client set the
049     *    returnECs boolean to TRUE in the PersistentSearch control, servers MUST
050     *    include an EntryChangeNotification control in the Controls portion of
051     *    each SearchResultEntry that is returned due to an entry being added,
052     *    deleted, or modified.
053     *    
054     *               EntryChangeNotification ::= SEQUENCE 
055     *               {
056     *                         changeType ENUMERATED 
057     *                         {
058     *                                 add             (1),
059     *                                 delete          (2),
060     *                                 modify          (4),
061     *                                 modDN           (8)
062     *                         },
063     *                         previousDN   LDAPDN OPTIONAL,     -- modifyDN ops. only
064     *                         changeNumber INTEGER OPTIONAL     -- if supported
065     *               }
066     *    
067     *    changeType indicates what LDAP operation caused the entry to be
068     *    returned.
069     *    
070     *    previousDN is present only for modifyDN operations and gives the DN of
071     *    the entry before it was renamed and/or moved.  Servers MUST include this
072     *    optional field only when returning change notifications as a result of
073     *    modifyDN operations.
074     * 
075     *    changeNumber is the change number [CHANGELOG] assigned by a server for
076     *    the change.  If a server supports an LDAP Change Log it SHOULD include
077     *    this field.
078     * </pre>
079     * 
080     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
081     * @version $Rev: 764131 $, $Date: 2009-04-11 03:03:00 +0200 (Sam, 11 avr 2009) $, 
082     */
083    public class EntryChangeControlCodec extends AbstractAsn1Object
084    {
085        public static final int UNDEFINED_CHANGE_NUMBER = -1;
086    
087        private ChangeType changeType = ChangeType.ADD;
088    
089        private long changeNumber = UNDEFINED_CHANGE_NUMBER;
090    
091        /** The previous DN */
092        private LdapDN previousDn = null;
093        
094        /** A temporary storage for the previous DN */
095        private byte[] previousDnBytes = null;
096    
097        /** The entry change global length */
098        private int eccSeqLength;
099    
100    
101        /**
102         * @see Asn1Object#Asn1Object
103         */
104        public EntryChangeControlCodec()
105        {
106            super();
107        }
108    
109        
110        /**
111         * Compute the EntryChangeControl length 
112         * 
113         * 0x30 L1 
114         *   | 
115         *   +--> 0x0A 0x0(1-4) [1|2|4|8] (changeType) 
116         *  [+--> 0x04 L2 previousDN] 
117         *  [+--> 0x02 0x0(1-4) [0..2^63-1] (changeNumber)]
118         */
119        public int computeLength()
120        {
121            int changeTypesLength = 1 + 1 + 1;
122    
123            int previousDnLength = 0;
124            int changeNumberLength = 0;
125    
126            if ( previousDn != null )
127            {
128                previousDnBytes = StringTools.getBytesUtf8( previousDn.getUpName() );
129                previousDnLength = 1 + TLV.getNbBytes( previousDnBytes.length ) + previousDnBytes.length;
130            }
131    
132            if ( changeNumber != UNDEFINED_CHANGE_NUMBER )
133            {
134                changeNumberLength = 1 + 1 + Value.getNbBytes( changeNumber );
135            }
136    
137            eccSeqLength = changeTypesLength + previousDnLength + changeNumberLength;
138    
139            return 1 + TLV.getNbBytes( eccSeqLength ) + eccSeqLength;
140        }
141    
142    
143        /**
144         * Encodes the entry change control.
145         * 
146         * @param buffer The encoded sink
147         * @return A ByteBuffer that contains the encoded PDU
148         * @throws EncoderException If anything goes wrong.
149         */
150        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
151        {
152            // Allocate the bytes buffer.
153            ByteBuffer bb = ByteBuffer.allocate( computeLength() );
154            bb.put( UniversalTag.SEQUENCE_TAG );
155            bb.put( TLV.getBytes( eccSeqLength ) );
156    
157            bb.put( UniversalTag.ENUMERATED_TAG );
158            bb.put( ( byte ) 1 );
159            bb.put( Value.getBytes( changeType.getValue() ) );
160    
161            if ( previousDn != null )
162            {
163                Value.encode( bb, previousDnBytes );
164            }
165            
166            if ( changeNumber != UNDEFINED_CHANGE_NUMBER )
167            {
168                Value.encode( bb, changeNumber );
169            }
170            
171            return bb;
172        }
173    
174    
175        /**
176         * Return a String representing this EntryChangeControl.
177         */
178        public String toString()
179        {
180            StringBuffer sb = new StringBuffer();
181    
182            sb.append( "    Entry Change Control\n" );
183            sb.append( "        changeType   : '" ).append( changeType ).append( "'\n" );
184            sb.append( "        previousDN   : '" ).append( previousDn ).append( "'\n" );
185            
186            if ( changeNumber == UNDEFINED_CHANGE_NUMBER )
187            {
188                sb.append( "        changeNumber : '" ).append( "UNDEFINED" ).append( "'\n" );
189            }
190            else
191            {
192                sb.append( "        changeNumber : '" ).append( changeNumber ).append( "'\n" );
193            }
194            
195            return sb.toString();
196        }
197    
198    
199        public ChangeType getChangeType()
200        {
201            return changeType;
202        }
203    
204    
205        public void setChangeType( ChangeType changeType )
206        {
207            this.changeType = changeType;
208        }
209    
210    
211        public LdapDN getPreviousDn()
212        {
213            return previousDn;
214        }
215    
216    
217        public void setPreviousDn( LdapDN previousDn )
218        {
219            this.previousDn = previousDn;
220        }
221    
222    
223        public long getChangeNumber()
224        {
225            return changeNumber;
226        }
227    
228    
229        public void setChangeNumber( long changeNumber )
230        {
231            this.changeNumber = changeNumber;
232        }
233    }