View Javadoc
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.extras.controls.syncrepl_impl;
21  
22  
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.directory.api.asn1.Asn1Object;
28  import org.apache.directory.api.asn1.DecoderException;
29  import org.apache.directory.api.asn1.EncoderException;
30  import org.apache.directory.api.asn1.ber.Asn1Decoder;
31  import org.apache.directory.api.asn1.ber.tlv.BerValue;
32  import org.apache.directory.api.asn1.ber.tlv.TLV;
33  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
34  import org.apache.directory.api.i18n.I18n;
35  import org.apache.directory.api.ldap.codec.api.ControlDecorator;
36  import org.apache.directory.api.ldap.codec.api.LdapApiService;
37  import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValue;
38  import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValueImpl;
39  import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SynchronizationInfoEnum;
40  import org.apache.directory.api.util.Strings;
41  
42  
43  /**
44   * A syncInfoValue object, as defined in RFC 4533
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public class SyncInfoValueDecorator extends ControlDecorator<SyncInfoValue> implements SyncInfoValue
49  {
50      /** The syncUUIDs cumulative length */
51      private int syncUUIDsLength;
52  
53      /** An instance of this decoder */
54      private static final Asn1Decoder DECODER = new Asn1Decoder();
55  
56      /** The global length for this control */
57      private int syncInfoValueLength;
58  
59  
60      /**
61       * The constructor for this codec. Dont't forget to set the type.
62       * 
63       * @param codec The LDAP Service to use
64       */
65      public SyncInfoValueDecorator( LdapApiService codec )
66      {
67          super( codec, new SyncInfoValueImpl() );
68      }
69  
70  
71      /**
72       * The constructor for this codec. Dont't forget to set the type.
73       * 
74       * @param codec The LDAP Service to use
75       * @param control The SyncInfoValue to decorate
76       */
77      public SyncInfoValueDecorator( LdapApiService codec, SyncInfoValue control )
78      {
79          super( codec, control );
80      }
81  
82  
83      /**
84       * The constructor for this codec.
85       * 
86       * @param codec The LDAP Service to use
87       * @param type The kind of syncInfo we will store. Can be newCookie,
88       * refreshPresent, refreshDelete or syncIdSet
89       */
90      public SyncInfoValueDecorator( LdapApiService codec, SynchronizationInfoEnum type )
91      {
92          this( codec );
93  
94          setType( type );
95      }
96  
97      
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     public SynchronizationInfoEnum getType()
103     {
104         return getDecorated().getType();
105     }
106 
107 
108     /**
109      * {@inheritDoc}
110      */
111     @Override
112     public void setType( SynchronizationInfoEnum type )
113     {
114         this.getDecorated().setType( type );
115 
116         // Initialize the arrayList if needed
117         if ( ( type == SynchronizationInfoEnum.SYNC_ID_SET ) && ( getDecorated().getSyncUUIDs() == null ) )
118         {
119             getDecorated().setSyncUUIDs( new ArrayList<byte[]>() );
120         }
121     }
122 
123 
124     /**
125      * {@inheritDoc}
126      */
127     @Override
128     public byte[] getCookie()
129     {
130         return getDecorated().getCookie();
131     }
132 
133 
134     /**
135      * {@inheritDoc}
136      */
137     @Override
138     public void setCookie( byte[] cookie )
139     {
140         // Copy the bytes
141         if ( !Strings.isEmpty( cookie ) )
142         {
143             byte[] copy = new byte[cookie.length];
144             System.arraycopy( cookie, 0, copy, 0, cookie.length );
145             getDecorated().setCookie( copy );
146         }
147         else
148         {
149             getDecorated().setCookie( null );
150         }
151     }
152 
153 
154     /**
155      * {@inheritDoc}
156      */
157     @Override
158     public boolean isRefreshDone()
159     {
160         return getDecorated().isRefreshDone();
161     }
162 
163 
164     /**
165      * {@inheritDoc}
166      */
167     @Override
168     public void setRefreshDone( boolean refreshDone )
169     {
170         getDecorated().setRefreshDone( refreshDone );
171     }
172 
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     public boolean isRefreshDeletes()
179     {
180         return getDecorated().isRefreshDeletes();
181     }
182 
183 
184     /**
185      * {@inheritDoc}
186      */
187     @Override
188     public void setRefreshDeletes( boolean refreshDeletes )
189     {
190         getDecorated().setRefreshDeletes( refreshDeletes );
191     }
192 
193 
194     /**
195      * {@inheritDoc}
196      */
197     @Override
198     public List<byte[]> getSyncUUIDs()
199     {
200         return getDecorated().getSyncUUIDs();
201     }
202 
203 
204     /**
205      * {@inheritDoc}
206      */
207     @Override
208     public void setSyncUUIDs( List<byte[]> syncUUIDs )
209     {
210         getDecorated().setSyncUUIDs( syncUUIDs );
211     }
212 
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
218     public void addSyncUUID( byte[] syncUUID )
219     {
220         getDecorated().addSyncUUID( syncUUID );
221     }
222 
223 
224     /**
225      * Compute the SyncInfoValue length.
226      * <br>
227      * SyncInfoValue :
228      * <pre>
229      * 0xA0 L1 abcd                   // newCookie
230      * 0xA1 L2                        // refreshDelete
231      *   |
232      *  [+--&gt; 0x04 L3 abcd]           // cookie
233      *  [+--&gt; 0x01 0x01 (0x00|0xFF)   // refreshDone
234      * 0xA2 L4                        // refreshPresent
235      *   |
236      *  [+--&gt; 0x04 L5 abcd]           // cookie
237      *  [+--&gt; 0x01 0x01 (0x00|0xFF)   // refreshDone
238      * 0xA3 L6                        // syncIdSet
239      *   |
240      *  [+--&gt; 0x04 L7 abcd]           // cookie
241      *  [+--&gt; 0x01 0x01 (0x00|0xFF)   // refreshDeletes
242      *   +--&gt; 0x31 L8                 // SET OF syncUUIDs
243      *          |
244      *         [+--&gt; 0x04 L9 abcd]    // syncUUID
245      * </pre>
246      * 
247      * @return The computed length
248      **/
249     @Override
250     public int computeLength()
251     {
252         // The mode length
253         syncInfoValueLength = 0;
254 
255         switch ( getType() )
256         {
257             case NEW_COOKIE:
258                 if ( getCookie() != null )
259                 {
260                     syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
261                 }
262                 else
263                 {
264                     syncInfoValueLength = 1 + 1;
265                 }
266 
267                 valueLength = syncInfoValueLength;
268 
269                 // Call the super class to compute the global control length
270                 return valueLength;
271 
272             case REFRESH_DELETE:
273             case REFRESH_PRESENT:
274                 if ( getCookie() != null )
275                 {
276                     syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
277                 }
278 
279                 // The refreshDone flag, only if not true, as it default to true
280                 if ( !isRefreshDone() )
281                 {
282                     syncInfoValueLength += 1 + 1 + 1;
283                 }
284 
285                 valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
286 
287                 // Call the super class to compute the global control length
288                 return valueLength;
289 
290             case SYNC_ID_SET:
291                 if ( getCookie() != null )
292                 {
293                     syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
294                 }
295 
296                 // The refreshDeletes flag, default to false
297                 if ( isRefreshDeletes() )
298                 {
299                     syncInfoValueLength += 1 + 1 + 1;
300                 }
301 
302                 // The syncUUIDs if any
303                 syncUUIDsLength = 0;
304 
305                 if ( !getSyncUUIDs().isEmpty() )
306                 {
307                     for ( byte[] syncUUID : getSyncUUIDs() )
308                     {
309                         int uuidLength = 1 + TLV.getNbBytes( syncUUID.length ) + syncUUID.length;
310 
311                         syncUUIDsLength += uuidLength;
312                     }
313                 }
314 
315                 syncInfoValueLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength;
316                 valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
317 
318                 // Call the super class to compute the global control length
319                 return valueLength;
320 
321             default:
322 
323         }
324 
325         return 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
326     }
327 
328 
329     /**
330      * Encode the SyncInfoValue control
331      *
332      * @param buffer The encoded sink
333      * @return A ByteBuffer that contains the encoded PDU
334      * @throws EncoderException If anything goes wrong.
335      */
336     @Override
337     public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
338     {
339         if ( buffer == null )
340         {
341             throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
342         }
343 
344         switch ( getType() )
345         {
346             case NEW_COOKIE:
347                 // The first case : newCookie
348                 buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
349 
350                 // As the OCTET_STRING is absorbed by the Application tag,
351                 // we have to store the L and V separately
352                 if ( ( getCookie() == null ) || ( getCookie().length == 0 ) )
353                 {
354                     buffer.put( ( byte ) 0 );
355                 }
356                 else
357                 {
358                     buffer.put( TLV.getBytes( getCookie().length ) );
359                     buffer.put( getCookie() );
360                 }
361 
362                 break;
363 
364             case REFRESH_DELETE:
365                 // The second case : refreshDelete
366                 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
367                 buffer.put( TLV.getBytes( syncInfoValueLength ) );
368 
369                 // The cookie, if any
370                 if ( getCookie() != null )
371                 {
372                     BerValue.encode( buffer, getCookie() );
373                 }
374 
375                 // The refreshDone flag
376                 if ( !isRefreshDone() )
377                 {
378                     BerValue.encode( buffer, isRefreshDone() );
379                 }
380 
381                 break;
382 
383             case REFRESH_PRESENT:
384                 // The third case : refreshPresent
385                 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
386                 buffer.put( TLV.getBytes( syncInfoValueLength ) );
387 
388                 // The cookie, if any
389                 if ( getCookie() != null )
390                 {
391                     BerValue.encode( buffer, getCookie() );
392                 }
393 
394                 // The refreshDone flag
395                 if ( !isRefreshDone() )
396                 {
397                     BerValue.encode( buffer, isRefreshDone() );
398                 }
399 
400                 break;
401 
402             case SYNC_ID_SET:
403                 // The last case : syncIdSet
404                 buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
405                 buffer.put( TLV.getBytes( syncInfoValueLength ) );
406 
407                 // The cookie, if any
408                 if ( getCookie() != null )
409                 {
410                     BerValue.encode( buffer, getCookie() );
411                 }
412 
413                 // The refreshDeletes flag if not false
414                 if ( isRefreshDeletes() )
415                 {
416                     BerValue.encode( buffer, isRefreshDeletes() );
417                 }
418 
419                 // The syncUUIDs
420                 buffer.put( UniversalTag.SET.getValue() );
421                 buffer.put( TLV.getBytes( syncUUIDsLength ) );
422 
423                 // Loop on the UUIDs if any
424                 if ( !getSyncUUIDs().isEmpty() )
425                 {
426                     for ( byte[] syncUUID : getSyncUUIDs() )
427                     {
428                         BerValue.encode( buffer, syncUUID );
429                     }
430                 }
431 
432                 break;
433 
434             default:
435                 throw new IllegalArgumentException( "Unexpected SynchronizationInfo: " + getType() );
436         }
437 
438         return buffer;
439     }
440 
441 
442     /**
443      * {@inheritDoc}
444      */
445     @Override
446     public byte[] getValue()
447     {
448         if ( value == null )
449         {
450             try
451             {
452                 computeLength();
453                 ByteBuffer buffer = ByteBuffer.allocate( valueLength );
454 
455                 switch ( getType() )
456                 {
457                     case NEW_COOKIE:
458                         // The first case : newCookie
459                         buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
460 
461                         // As the OCTET_STRING is absorbed by the Application tag,
462                         // we have to store the L and V separately
463                         if ( ( getCookie() == null ) || ( getCookie().length == 0 ) )
464                         {
465                             buffer.put( ( byte ) 0 );
466                         }
467                         else
468                         {
469                             buffer.put( TLV.getBytes( getCookie().length ) );
470                             buffer.put( getCookie() );
471                         }
472 
473                         break;
474 
475                     case REFRESH_DELETE:
476                         // The second case : refreshDelete
477                         buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
478                         buffer.put( TLV.getBytes( syncInfoValueLength ) );
479 
480                         // The cookie, if any
481                         if ( getCookie() != null )
482                         {
483                             BerValue.encode( buffer, getCookie() );
484                         }
485 
486                         // The refreshDone flag
487                         if ( !isRefreshDone() )
488                         {
489                             BerValue.encode( buffer, isRefreshDone() );
490                         }
491 
492                         break;
493 
494                     case REFRESH_PRESENT:
495                         // The third case : refreshPresent
496                         buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
497                         buffer.put( TLV.getBytes( syncInfoValueLength ) );
498 
499                         // The cookie, if any
500                         if ( getCookie() != null )
501                         {
502                             BerValue.encode( buffer, getCookie() );
503                         }
504 
505                         // The refreshDone flag
506                         if ( !isRefreshDone() )
507                         {
508                             BerValue.encode( buffer, isRefreshDone() );
509                         }
510 
511                         break;
512 
513                     case SYNC_ID_SET:
514                         // The last case : syncIdSet
515                         buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
516                         buffer.put( TLV.getBytes( syncInfoValueLength ) );
517 
518                         // The cookie, if any
519                         if ( getCookie() != null )
520                         {
521                             BerValue.encode( buffer, getCookie() );
522                         }
523 
524                         // The refreshDeletes flag if not false
525                         if ( isRefreshDeletes() )
526                         {
527                             BerValue.encode( buffer, isRefreshDeletes() );
528                         }
529 
530                         // The syncUUIDs
531                         buffer.put( UniversalTag.SET.getValue() );
532                         buffer.put( TLV.getBytes( syncUUIDsLength ) );
533 
534                         // Loop on the UUIDs if any
535                         if ( !getSyncUUIDs().isEmpty() )
536                         {
537                             for ( byte[] syncUUID : getSyncUUIDs() )
538                             {
539                                 BerValue.encode( buffer, syncUUID );
540                             }
541                         }
542 
543                         break;
544 
545                     default:
546                         throw new IllegalArgumentException( "Unexpected SynchronizationInfo: " + getType() );
547                 }
548 
549                 value = buffer.array();
550             }
551             catch ( EncoderException e )
552             {
553                 return null;
554             }
555         }
556 
557         return value;
558     }
559 
560 
561     /**
562      * {@inheritDoc}
563      */
564     @Override
565     public Asn1Object decode( byte[] controlBytes ) throws DecoderException
566     {
567         ByteBuffer bb = ByteBuffer.wrap( controlBytes );
568         SyncInfoValueContainer container = new SyncInfoValueContainer( getCodecService(), this );
569         DECODER.decode( bb, container );
570         return this;
571     }
572 
573 
574     /**
575      * @see Object#toString()
576      */
577     @Override
578     public String toString()
579     {
580         return getDecorated().toString();
581     }
582 }