1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.directory.api.ldap.codec.decorators;
21
22
23 import java.nio.BufferOverflowException;
24 import java.nio.ByteBuffer;
25 import java.util.LinkedList;
26 import java.util.List;
27
28 import org.apache.directory.api.asn1.EncoderException;
29 import org.apache.directory.api.asn1.ber.tlv.BerValue;
30 import org.apache.directory.api.asn1.ber.tlv.TLV;
31 import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
32 import org.apache.directory.api.i18n.I18n;
33 import org.apache.directory.api.ldap.codec.api.LdapApiService;
34 import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
35 import org.apache.directory.api.ldap.model.entry.Attribute;
36 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
37 import org.apache.directory.api.ldap.model.entry.Entry;
38 import org.apache.directory.api.ldap.model.entry.Value;
39 import org.apache.directory.api.ldap.model.exception.LdapException;
40 import org.apache.directory.api.ldap.model.message.AddRequest;
41 import org.apache.directory.api.ldap.model.message.Control;
42 import org.apache.directory.api.ldap.model.name.Dn;
43 import org.apache.directory.api.util.Strings;
44
45
46 /**
47 * A decorator for the AddRequest message
48 *
49 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50 */
51 public final class AddRequestDecorator extends SingleReplyRequestDecorator<AddRequest> implements
52 AddRequest
53 {
54 /** The add request length */
55 private int addRequestLength;
56
57 /** The Entry length */
58 private int entryLength;
59
60 /** The list of all attributes length */
61 private List<Integer> attributesLength;
62
63 /** The list of all attributes Id bytes */
64 private List<byte[]> attributeIds;
65
66 /** The list of all vals length */
67 private List<Integer> valuesLength;
68
69 /** The current attribute being decoded */
70 private Attribute currentAttribute;
71
72 /** The bytes containing the Dn */
73 private byte[] dnBytes;
74
75
76 /**
77 * Makes a AddRequest a MessageDecorator.
78 *
79 * @param codec The LDAP service instance
80 * @param decoratedMessage the decorated AddRequest
81 */
82 public AddRequestDecorator( LdapApiService codec, AddRequest decoratedMessage )
83 {
84 super( codec, decoratedMessage );
85 }
86
87
88 /**
89 * {@inheritDoc}
90 */
91 @Override
92 public AddRequest setMessageId( int messageId )
93 {
94 super.setMessageId( messageId );
95
96 return this;
97 }
98
99
100 /**
101 * {@inheritDoc}
102 */
103 @Override
104 public AddRequest addControl( Control control )
105 {
106 return ( AddRequest ) super.addControl( control );
107 }
108
109
110 /**
111 * {@inheritDoc}
112 */
113 @Override
114 public AddRequest addAllControls( Control[] controls )
115 {
116 return ( AddRequest ) super.addAllControls( controls );
117 }
118
119
120 /**
121 * {@inheritDoc}
122 */
123 @Override
124 public AddRequest removeControl( Control control )
125 {
126 return ( AddRequest ) super.removeControl( control );
127 }
128
129
130 //-------------------------------------------------------------------------
131 // The AddRequest methods
132 //-------------------------------------------------------------------------
133
134 /**
135 * {@inheritDoc}
136 */
137 @Override
138 public Dn getEntryDn()
139 {
140 return getDecorated().getEntryDn();
141 }
142
143
144 /**
145 * {@inheritDoc}
146 */
147 @Override
148 public AddRequest setEntryDn( Dn entry )
149 {
150 getDecorated().setEntryDn( entry );
151
152 return this;
153 }
154
155
156 /**
157 * {@inheritDoc}
158 */
159 @Override
160 public Entry getEntry()
161 {
162 return getDecorated().getEntry();
163 }
164
165
166 /**
167 * {@inheritDoc}
168 */
169 @Override
170 public AddRequest setEntry( Entry entry )
171 {
172 getDecorated().setEntry( entry );
173
174 return this;
175 }
176
177
178 /**
179 * Create a new attributeValue
180 *
181 * @param type The attribute's name (called 'type' in the grammar)
182 * @throws LdapException If the value is invalid
183 */
184 public void addAttributeType( String type ) throws LdapException
185 {
186 // do not create a new attribute if we have seen this attributeType before
187 if ( getDecorated().getEntry().get( type ) != null )
188 {
189 currentAttribute = getDecorated().getEntry().get( type );
190 return;
191 }
192
193 // fix this to use AttributeImpl(type.getString().toLowerCase())
194 currentAttribute = new DefaultAttribute( type );
195 getDecorated().getEntry().put( currentAttribute );
196 }
197
198
199 /**
200 * @return Returns the currentAttribute type.
201 */
202 public String getCurrentAttributeType()
203 {
204 return currentAttribute.getUpId();
205 }
206
207
208 /**
209 * Add a new value to the current attribute
210 *
211 * @param value The value to add
212 * @throws LdapException If the value is invalid
213 */
214 public void addAttributeValue( String value ) throws LdapException
215 {
216 currentAttribute.add( value );
217 }
218
219
220 /**
221 * Add a new value to the current attribute
222 *
223 * @param value The value to add
224 * @throws LdapException If the value is invalid
225 */
226 public void addAttributeValue( Value<?> value ) throws LdapException
227 {
228 currentAttribute.add( value );
229 }
230
231
232 /**
233 * Add a new value to the current attribute
234 *
235 * @param value The value to add
236 * @throws LdapException If the value is invalid
237 */
238 public void addAttributeValue( byte[] value ) throws LdapException
239 {
240 currentAttribute.add( value );
241 }
242
243
244 //-------------------------------------------------------------------------
245 // The Decorator methods
246 //-------------------------------------------------------------------------
247 /**
248 * Compute the AddRequest length
249 * <br>
250 * AddRequest :
251 * <pre>
252 * 0x68 L1
253 * |
254 * +--> 0x04 L2 entry
255 * +--> 0x30 L3 (attributes)
256 * |
257 * +--> 0x30 L4-1 (attribute)
258 * | |
259 * | +--> 0x04 L5-1 type
260 * | +--> 0x31 L6-1 (values)
261 * | |
262 * | +--> 0x04 L7-1-1 value
263 * | +--> ...
264 * | +--> 0x04 L7-1-n value
265 * |
266 * +--> 0x30 L4-2 (attribute)
267 * | |
268 * | +--> 0x04 L5-2 type
269 * | +--> 0x31 L6-2 (values)
270 * | |
271 * | +--> 0x04 L7-2-1 value
272 * | +--> ...
273 * | +--> 0x04 L7-2-n value
274 * |
275 * +--> ...
276 * |
277 * +--> 0x30 L4-m (attribute)
278 * |
279 * +--> 0x04 L5-m type
280 * +--> 0x31 L6-m (values)
281 * |
282 * +--> 0x04 L7-m-1 value
283 * +--> ...
284 * +--> 0x04 L7-m-n value
285 * </pre>
286 */
287 @Override
288 public int computeLength()
289 {
290 AddRequest addRequest = getDecorated();
291 Entry entry = addRequest.getEntry();
292
293 if ( entry == null )
294 {
295 throw new IllegalArgumentException( I18n.err( I18n.ERR_04481_ENTRY_NULL_VALUE ) );
296 }
297
298 dnBytes = Strings.getBytesUtf8( entry.getDn().getName() );
299 int dnLen = dnBytes.length;
300
301 // The entry Dn
302 addRequestLength = 1 + TLV.getNbBytes( dnLen ) + dnLen;
303
304 // The attributes sequence
305 entryLength = 0;
306
307 if ( entry.size() != 0 )
308 {
309 attributesLength = new LinkedList<>();
310 attributeIds = new LinkedList<>();
311 valuesLength = new LinkedList<>();
312
313 // Compute the attributes length
314 for ( Attribute attribute : entry )
315 {
316 int localAttributeLength;
317 int localValuesLength;
318
319 // Get the type length
320 byte[] attributeIdBytes = Strings.getBytesUtf8( attribute.getUpId() );
321 attributeIds.add( attributeIdBytes );
322
323 int idLength = attributeIdBytes.length;
324 localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
325
326 // The values
327 if ( attribute.size() != 0 )
328 {
329 localValuesLength = 0;
330
331 for ( Value<?> value : attribute )
332 {
333 if ( value.getBytes() == null )
334 {
335 localValuesLength += 1 + 1;
336 }
337 else
338 {
339 int valueLength = value.getBytes().length;
340 localValuesLength += 1 + TLV.getNbBytes( valueLength ) + valueLength;
341 }
342 }
343
344 localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
345 }
346 else
347 {
348 // No value : we still have to store the encapsulating Sequence
349 localValuesLength = 1 + 1;
350 localAttributeLength += 1 + 1 + localValuesLength;
351 }
352
353 // add the attribute length to the attributes length
354 entryLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
355
356 attributesLength.add( localAttributeLength );
357 valuesLength.add( localValuesLength );
358 }
359 }
360
361 addRequestLength += 1 + TLV.getNbBytes( entryLength ) + entryLength;
362
363 // Return the result.
364 return 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
365 }
366
367
368 /**
369 * Encode the AddRequest message to a PDU.
370 * <br>
371 * AddRequest :
372 * <pre>
373 * 0x68 LL
374 * 0x04 LL entry
375 * 0x30 LL attributesList
376 * 0x30 LL attributeList
377 * 0x04 LL attributeDescription
378 * 0x31 LL attributeValues
379 * 0x04 LL attributeValue
380 * ...
381 * 0x04 LL attributeValue
382 * ...
383 * 0x30 LL attributeList
384 * 0x04 LL attributeDescription
385 * 0x31 LL attributeValue
386 * 0x04 LL attributeValue
387 * ...
388 * 0x04 LL attributeValue
389 * </pre>
390 *
391 * @param buffer The buffer where to put the PDU
392 */
393 @Override
394 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
395 {
396 try
397 {
398 // The AddRequest Tag
399 buffer.put( LdapCodecConstants.ADD_REQUEST_TAG );
400 buffer.put( TLV.getBytes( addRequestLength ) );
401
402 // The entry
403 BerValue.encode( buffer, dnBytes );
404
405 // The attributes sequence
406 buffer.put( UniversalTag.SEQUENCE.getValue() );
407 buffer.put( TLV.getBytes( entryLength ) );
408
409 // The partial attribute list
410 Entry entry = getEntry();
411
412 if ( entry.size() != 0 )
413 {
414 int attributeNumber = 0;
415
416 // Compute the attributes length
417 for ( Attribute attribute : entry )
418 {
419 // The attributes list sequence
420 buffer.put( UniversalTag.SEQUENCE.getValue() );
421 int localAttributeLength = attributesLength.get( attributeNumber );
422 buffer.put( TLV.getBytes( localAttributeLength ) );
423
424 // The attribute type
425 BerValue.encode( buffer, attributeIds.get( attributeNumber ) );
426
427 // The values
428 buffer.put( UniversalTag.SET.getValue() );
429 int localValuesLength = valuesLength.get( attributeNumber );
430 buffer.put( TLV.getBytes( localValuesLength ) );
431
432 if ( attribute.size() != 0 )
433 {
434 for ( Value<?> value : attribute )
435 {
436 BerValue.encode( buffer, value.getBytes() );
437 }
438 }
439 else
440 {
441 BerValue.encode( buffer, Strings.EMPTY_BYTES );
442 }
443
444 // Go to the next attribute number
445 attributeNumber++;
446 }
447 }
448
449 return buffer;
450 }
451 catch ( BufferOverflowException boe )
452 {
453 throw new EncoderException( "The PDU buffer size is too small !", boe );
454 }
455 }
456 }