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 "2.16.840.1.113730.3.4.7". 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 }