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;
021
022
023 import java.nio.BufferOverflowException;
024 import java.nio.ByteBuffer;
025 import java.util.ArrayList;
026 import java.util.HashMap;
027 import java.util.List;
028 import java.util.Map;
029
030 import org.apache.directory.shared.asn1.AbstractAsn1Object;
031 import org.apache.directory.shared.asn1.ber.tlv.TLV;
032 import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
033 import org.apache.directory.shared.asn1.ber.tlv.Value;
034 import org.apache.directory.shared.asn1.codec.EncoderException;
035 import org.apache.directory.shared.i18n.I18n;
036 import org.apache.directory.shared.ldap.codec.controls.CodecControl;
037 import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
038 import org.apache.directory.shared.ldap.codec.controls.replication.syncDoneValue.SyncDoneValueControl;
039 import org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue.SyncInfoValueControl;
040 import org.apache.directory.shared.ldap.codec.controls.replication.syncRequestValue.SyncRequestValueControl;
041 import org.apache.directory.shared.ldap.codec.controls.replication.syncStateValue.SyncStateValueControl;
042 import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl;
043 import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl;
044 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
045 import org.apache.directory.shared.ldap.message.control.Control;
046 import org.apache.directory.shared.ldap.message.control.replication.SynchronizationInfoEnum;
047
048
049 /**
050 * The main ldapObject : every Ldap Message are encapsulated in it. It contains
051 * a message Id, a operation (protocolOp) and one ore more Controls.
052 *
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 * @version $Rev: 912399 $, $Date: 2010-02-21 22:52:31 +0200 (Sun, 21 Feb 2010) $,
055 */
056 public abstract class LdapMessageCodec extends AbstractAsn1Object
057 {
058 // ~ Instance fields
059 // ----------------------------------------------------------------------------
060
061 /** The message ID */
062 private int messageId;
063
064 /** The controls */
065 private List<Control> controls;
066
067 /** The current control */
068 private Control currentControl;
069
070 /** The LdapMessage length */
071 protected int ldapMessageLength;
072
073 /** The controls length */
074 private int controlsLength;
075
076 /** The controls sequence length */
077 private int controlsSequenceLength;
078
079 private Map<String, Control> codecControls = new HashMap<String, Control>();
080
081
082 // ~ Constructors
083 // -------------------------------------------------------------------------------
084
085 /**
086 * Creates a new LdapMessage object.
087 */
088 public LdapMessageCodec()
089 {
090 super();
091 // We should not create this kind of object directly
092
093 // Initialize the different known Controls
094 Control control = new PersistentSearchControl();
095 codecControls.put( control.getOid(), control );
096
097 control = new ManageDsaITControl();
098 codecControls.put( control.getOid(), control );
099
100 control = new SubentriesControl();
101 codecControls.put( control.getOid(), control );
102
103 control = new PagedResultsControl();
104 codecControls.put( control.getOid(), control );
105
106 control = new SyncDoneValueControl();
107 codecControls.put( control.getOid(), control );
108
109 control = new SyncInfoValueControl( SynchronizationInfoEnum.NEW_COOKIE );
110 codecControls.put( control.getOid(), control );
111
112 control = new SyncInfoValueControl( SynchronizationInfoEnum.REFRESH_DELETE );
113 codecControls.put( control.getOid(), control );
114
115 control = new SyncInfoValueControl( SynchronizationInfoEnum.REFRESH_PRESENT );
116 codecControls.put( control.getOid(), control );
117
118 control = new SyncInfoValueControl( SynchronizationInfoEnum.SYNC_ID_SET );
119 codecControls.put( control.getOid(), control );
120
121 control = new SyncRequestValueControl();
122 codecControls.put( control.getOid(), control );
123
124 control = new SyncStateValueControl();
125 codecControls.put( control.getOid(), control );
126 }
127
128
129 // ~ Methods
130 // ------------------------------------------------------------------------------------
131
132 /**
133 * Get the Control Object at a specific index
134 *
135 * @param i The index of the Control Object to get
136 * @return The selected Control Object
137 */
138 public Control getControls( int i )
139 {
140 if ( controls != null )
141 {
142 return controls.get( i );
143 }
144 else
145 {
146 return null;
147 }
148 }
149
150
151 /**
152 * Get the Control Objects
153 *
154 * @return The Control Objects
155 */
156 public List<Control> getControls()
157 {
158 return controls;
159 }
160
161
162 /**
163 * Get the current Control Object
164 *
165 * @return The current Control Object
166 */
167 public Control getCurrentControl()
168 {
169 return currentControl;
170 }
171
172
173 public Control getCodecControl( String oid )
174 {
175 return codecControls.get( oid );
176 }
177
178
179 /**
180 * Add a control to the Controls array
181 *
182 * @param control The Control to add
183 */
184 public void addControl( Control control )
185 {
186 currentControl = control;
187
188 if ( controls == null )
189 {
190 controls = new ArrayList<Control>();
191 }
192
193 controls.add( control );
194 }
195
196
197 /**
198 * Set or add a list of controls to the Controls array. If the existing
199 * control array is not null then the given controls will be added
200 *
201 * @param controls The list of Controls to set or add
202 */
203 public void addControls( List<Control> controls )
204 {
205 if( this.controls == null )
206 {
207 this.controls = controls;
208 }
209 else if( controls != null )
210 {
211 this.controls.addAll( controls );
212 }
213 }
214
215
216 /**
217 * Init the controls array
218 */
219 public void initControls()
220 {
221 controls = new ArrayList<Control>();
222 }
223
224
225 /**
226 * Get the message ID
227 *
228 * @return The message ID
229 */
230 public int getMessageId()
231 {
232 return messageId;
233 }
234
235
236 /**
237 * Set the message ID
238 *
239 * @param messageId The message ID
240 */
241 public void setMessageId( int messageId )
242 {
243 this.messageId = messageId;
244 }
245
246
247 /**
248 * Get the message type
249 *
250 * @return The message type
251 */
252 public abstract MessageTypeEnum getMessageType();
253
254
255 /**
256 * Get the message type Name
257 *
258 * @return The message type name
259 */
260 public abstract String getMessageTypeName();
261
262
263 protected abstract int computeLengthProtocolOp();
264
265
266 /**
267 * Compute the LdapMessage length LdapMessage :
268 * 0x30 L1
269 * |
270 * +--> 0x02 0x0(1-4) [0..2^31-1] (MessageId)
271 * +--> protocolOp
272 * [+--> Controls]
273 *
274 * MessageId length = Length(0x02) + length(MessageId) + MessageId.length
275 * L1 = length(ProtocolOp)
276 * LdapMessage length = Length(0x30) + Length(L1) + MessageId length + L1
277 */
278 public int computeLength()
279 {
280 // The length of the MessageId. It's the sum of
281 // - the tag (0x02), 1 byte
282 // - the length of the Id length, 1 byte
283 // - the Id length, 1 to 4 bytes
284 ldapMessageLength = 1 + 1 + Value.getNbBytes( messageId );
285
286 // Get the protocolOp length
287 int protocolOpLength = computeLengthProtocolOp();
288
289 // Add the protocol length to the message length
290 ldapMessageLength += protocolOpLength;
291
292 // Do the same thing for Controls, if any.
293 if ( controls != null )
294 {
295 // Controls :
296 // 0xA0 L3
297 // |
298 // +--> 0x30 L4
299 // +--> 0x30 L5
300 // +--> ...
301 // +--> 0x30 Li
302 // +--> ...
303 // +--> 0x30 Ln
304 //
305 // L3 = Length(0x30) + Length(L5) + L5
306 // + Length(0x30) + Length(L6) + L6
307 // + ...
308 // + Length(0x30) + Length(Li) + Li
309 // + ...
310 // + Length(0x30) + Length(Ln) + Ln
311 //
312 // LdapMessageLength = LdapMessageLength + Length(0x90)
313 // + Length(L3) + L3
314 controlsSequenceLength = 0;
315
316 // We may have more than one control. ControlsLength is L4.
317 for ( Control control:controls )
318 {
319 controlsSequenceLength += ((CodecControl)control).computeLength();
320 }
321
322 // Computes the controls length
323 controlsLength = controlsSequenceLength; // 1 + Length.getNbBytes(
324 // controlsSequenceLength
325 // ) + controlsSequenceLength;
326
327 // Now, add the tag and the length of the controls length
328 ldapMessageLength += 1 + TLV.getNbBytes( controlsSequenceLength ) + controlsSequenceLength;
329 }
330
331 // finally, calculate the global message size :
332 // length(Tag) + Length(length) + length
333
334 return 1 + ldapMessageLength + TLV.getNbBytes( ldapMessageLength );
335 }
336
337
338 protected abstract void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException;
339
340 /**
341 * Generate the PDU which contains the encoded object.
342 *
343 * The generation is done in two phases :
344 * - first, we compute the length of each part and the
345 * global PDU length
346 * - second, we produce the PDU.
347 *
348 * <pre>
349 * 0x30 L1
350 * |
351 * +--> 0x02 L2 MessageId
352 * +--> ProtocolOp
353 * +--> Controls
354 *
355 * L2 = Length(MessageId)
356 * L1 = Length(0x02) + Length(L2) + L2 + Length(ProtocolOp) + Length(Controls)
357 * LdapMessageLength = Length(0x30) + Length(L1) + L1
358 * </pre>
359 *
360 * @param buffer The encoded PDU
361 * @return A ByteBuffer that contaons the PDU
362 * @throws EncoderException If anything goes wrong.
363 */
364 public ByteBuffer encode() throws EncoderException
365 {
366 // Allocate the bytes buffer.
367 ByteBuffer bb = ByteBuffer.allocate( computeLength() );
368
369 try
370 {
371 // The LdapMessage Sequence
372 bb.put( UniversalTag.SEQUENCE_TAG );
373
374 // The length has been calculated by the computeLength method
375 bb.put( TLV.getBytes( ldapMessageLength ) );
376 }
377 catch ( BufferOverflowException boe )
378 {
379 throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
380 }
381
382 // The message Id
383 Value.encode( bb, messageId );
384
385 // Add the protocolOp part
386 encodeProtocolOp( bb );
387
388 // Do the same thing for Controls, if any.
389 if ( controls != null )
390 {
391 // Encode the controls
392 bb.put( ( byte ) LdapConstants.CONTROLS_TAG );
393 bb.put( TLV.getBytes( controlsLength ) );
394
395 // Encode each control
396 for ( Control control:controls )
397 {
398 ((CodecControl)control).encode( bb );
399 }
400 }
401
402 return bb;
403 }
404
405
406 /**
407 * Get a String representation of a LdapMessage
408 *
409 * @return A LdapMessage String
410 */
411 protected String toString( String protocolOp )
412 {
413 StringBuffer sb = new StringBuffer();
414
415 sb.append( "LdapMessage\n" );
416 sb.append( " message Id : " ).append( messageId ).append( '\n' );
417
418 sb.append( protocolOp ).append( '\n' );
419
420 if ( controls != null )
421 {
422 for ( Control control:controls )
423 {
424 sb.append( control );
425 }
426 }
427
428 return sb.toString();
429 }
430 }