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.controls.replication.syncInfoValue;
021    
022    import java.nio.ByteBuffer;
023    import java.util.ArrayList;
024    import java.util.List;
025    
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.i18n.I18n;
031    import org.apache.directory.shared.ldap.codec.controls.AbstractControl;
032    import org.apache.directory.shared.ldap.message.control.replication.SynchronizationInfoEnum;
033    import org.apache.directory.shared.ldap.util.StringTools;
034    
035    /**
036     * A syncInfoValue object, as defined in RFC 4533
037     * 
038     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039     * @version $Rev:$, $Date: 
040     */
041    public class SyncInfoValueControl extends AbstractControl
042    {
043        /** This control OID */
044        public static final String CONTROL_OID = "1.3.6.1.4.1.4203.1.9.1.4";
045    
046        /** The kind of syncInfoValue we are dealing with */
047        private SynchronizationInfoEnum type;
048        
049        /** The cookie */
050        private byte[] cookie;
051        
052        /** The refreshDone flag if we are dealing with refreshXXX syncInfo. Default to true */
053        private boolean refreshDone = true;
054        
055        /** The refreshDeletes flag if we are dealing with syncIdSet syncInfo. Defaluts to false */
056        private boolean refreshDeletes = false;
057        
058        /** The list of UUIDs if we are dealing with syncIdSet syncInfo */
059        private List<byte[]> syncUUIDs;
060        
061        /** The syncUUIDs cumulative lentgh */
062        private int syncUUIDsLength;
063        
064        
065        /**
066         * The constructor for this codec.
067         * @param type The kind of syncInfo we will store. Can be newCookie, 
068         * refreshPresent, refreshDelete or syncIdSet
069         */
070        public SyncInfoValueControl( SynchronizationInfoEnum type )
071        {
072            super( CONTROL_OID );
073    
074            decoder = new SyncInfoValueControlDecoder();
075            this.type = type;
076            
077            // Initialize the arrayList if needed
078            if ( type == SynchronizationInfoEnum.SYNC_ID_SET )
079            {
080                syncUUIDs = new ArrayList<byte[]>();
081            }
082        }
083        
084        
085        /** The global length for this control */
086        private int syncInfoValueLength;
087    
088        /**
089         * Get the control type.
090         * 
091         * @return the type : one of newCookie, refreshDelete, refreshPresent or syncIdSet
092         */
093        public SynchronizationInfoEnum getType()
094        {
095            return type;
096        }
097    
098        
099        /**
100         * @param syncMode the syncMode to set
101         */
102        public void setType( SynchronizationInfoEnum type )
103        {
104            this.type = type;
105        }
106    
107        
108        /**
109         * @return the cookie
110         */
111        public byte[] getCookie()
112        {
113            return cookie;
114        }
115    
116        
117        /**
118         * @param cookie the cookie to set
119         */
120        public void setCookie( byte[] cookie )
121        {
122            this.cookie = cookie;
123        }
124    
125    
126        /**
127         * @return the refreshDone
128         */
129        public boolean isRefreshDone()
130        {
131            return refreshDone;
132        }
133    
134    
135        /**
136         * @param refreshDone the refreshDone to set
137         */
138        public void setRefreshDone( boolean refreshDone )
139        {
140            this.refreshDone = refreshDone;
141        }
142    
143    
144        /**
145         * @return the refreshDeletes
146         */
147        public boolean isRefreshDeletes()
148        {
149            return refreshDeletes;
150        }
151    
152    
153        /**
154         * @param refreshDeletes the refreshDeletes to set
155         */
156        public void setRefreshDeletes( boolean refreshDeletes )
157        {
158            this.refreshDeletes = refreshDeletes;
159        }
160    
161    
162        /**
163         * @return the syncUUIDs
164         */
165        public List<byte[]> getSyncUUIDs()
166        {
167            return syncUUIDs;
168        }
169    
170    
171        /**
172         * @param syncUUIDs the syncUUIDs to set
173         */
174        public void setSyncUUIDs( List<byte[]> syncUUIDs )
175        {
176            this.syncUUIDs = syncUUIDs;
177        }
178        
179        
180        /**
181         * @param syncUUIDs the syncUUIDs to set
182         */
183        public void addSyncUUID( byte[] syncUUID )
184        {
185            syncUUIDs.add( syncUUID );
186        }
187    
188        
189    
190        
191        /**
192         * Compute the SyncInfoValue length.
193         * 
194         * SyncInfoValue :
195         * 
196         * 0xA0 L1 abcd                   // newCookie
197         * 0xA1 L2                        // refreshDelete
198         *   |
199         *  [+--> 0x04 L3 abcd]           // cookie
200         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
201         * 0xA2 L4                        // refreshPresent
202         *   |
203         *  [+--> 0x04 L5 abcd]           // cookie
204         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
205         * 0xA3 L6                        // syncIdSet
206         *   |
207         *  [+--> 0x04 L7 abcd]           // cookie
208         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDeletes
209         *   +--> 0x31 L8                 // SET OF syncUUIDs
210         *          |
211         *         [+--> 0x04 L9 abcd]    // syncUUID    public static final int AND_FILTER_TAG = 0xA0;
212    
213        public static final int OR_FILTER_TAG = 0xA1;
214    
215        public static final int NOT_FILTER_TAG = 0xA2;
216    
217        public static final int BIND_REQUEST_SASL_TAG = 0xA3;
218    
219         */
220        public int computeLength()
221        {
222            // The mode length
223            syncInfoValueLength = 0;
224            
225            switch ( type )
226            {
227                case NEW_COOKIE :
228                    if ( cookie != null )
229                    {
230                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
231                    }
232                    else
233                    {
234                        syncInfoValueLength = 1 + 1;
235                    }
236                    
237                    valueLength = syncInfoValueLength;
238    
239                    // Call the super class to compute the global control length
240                    return super.computeLength( valueLength );
241                    
242                case REFRESH_DELETE :
243                case REFRESH_PRESENT :
244                    if ( cookie != null )
245                    {
246                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
247                    }
248                    
249                    // The refreshDone flag, only if not true, as it default to true
250                    if ( !refreshDone )
251                    {
252                        syncInfoValueLength += 1 + 1 + 1;
253                    }
254                    
255                    valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
256                    
257                    // Call the super class to compute the global control length
258                    return super.computeLength( valueLength );
259                    
260                case SYNC_ID_SET :
261                    if ( cookie != null )
262                    {
263                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
264                    }
265                    
266                    // The refreshDeletes flag, default to false
267                    if ( refreshDeletes )
268                    {
269                        syncInfoValueLength += 1 + 1 + 1;
270                    }
271    
272                    // The syncUUIDs if any
273                    syncUUIDsLength = 0;
274    
275                    if ( syncUUIDs.size() != 0 )
276                    {
277                        for ( byte[] syncUUID:syncUUIDs )
278                        {
279                            int uuidLength = 1 + TLV.getNbBytes( syncUUID.length ) + syncUUID.length;
280                            
281                            syncUUIDsLength += uuidLength;
282                        }
283                    }
284                    
285                    syncInfoValueLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength;
286                    valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
287    
288                    // Call the super class to compute the global control length
289                    return super.computeLength( valueLength );
290            }
291            
292            return 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
293        }
294        
295        
296        /**
297         * Encode the SyncInfoValue control
298         * 
299         * @param buffer The encoded sink
300         * @return A ByteBuffer that contains the encoded PDU
301         * @throws EncoderException If anything goes wrong.
302         */
303        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
304        {
305            if ( buffer == null )
306            {
307                throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
308            }
309    
310            // Encode the Control envelop
311            super.encode( buffer );
312            
313            // Encode the OCTET_STRING tag
314            buffer.put( UniversalTag.OCTET_STRING_TAG );
315            buffer.put( TLV.getBytes( valueLength ) );
316    
317            switch ( type )
318            {
319                case NEW_COOKIE :
320                    // The first case : newCookie
321                    buffer.put( (byte)SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
322    
323                    // As the OCTET_STRING is absorbed by the Application tag,
324                    // we have to store the L and V separately
325                    if ( ( cookie == null ) || ( cookie.length == 0 ) )
326                    {
327                        buffer.put( ( byte ) 0 );
328                    }
329                    else
330                    {
331                        buffer.put( TLV.getBytes( cookie.length ) );
332                        buffer.put( cookie );
333                    }
334    
335                    break;
336                    
337                case REFRESH_DELETE :
338                    // The second case : refreshDelete
339                    buffer.put( (byte)SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
340                    buffer.put( TLV.getBytes( syncInfoValueLength ) );
341    
342                    // The cookie, if any
343                    if ( cookie != null )
344                    {
345                        Value.encode( buffer, cookie );
346                    }
347                    
348                    // The refreshDone flag
349                    if ( !refreshDone )
350                    {
351                        Value.encode( buffer, refreshDone );
352                    }
353                    
354                    break;
355                    
356                case REFRESH_PRESENT :
357                    // The third case : refreshPresent
358                    buffer.put( (byte)SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
359                    buffer.put( TLV.getBytes( syncInfoValueLength ) );
360    
361                    // The cookie, if any
362                    if ( cookie != null )
363                    {
364                        Value.encode( buffer, cookie );
365                    }
366                    
367                    // The refreshDone flag
368                    if ( !refreshDone )
369                    {
370                        Value.encode( buffer, refreshDone );
371                    }
372    
373                    break;
374                    
375                case SYNC_ID_SET :
376                    // The last case : syncIdSet
377                    buffer.put( (byte)SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
378                    buffer.put( TLV.getBytes( syncInfoValueLength ) );
379    
380                    // The cookie, if any
381                    if ( cookie != null )
382                    {
383                        Value.encode( buffer, cookie );
384                    }
385                    
386                    // The refreshDeletes flag if not false
387                    if ( refreshDeletes )
388                    {
389                        Value.encode( buffer, refreshDeletes );
390                    }
391                    
392                    // The syncUUIDs
393                    buffer.put( UniversalTag.SET_TAG );
394                    buffer.put( TLV.getBytes( syncUUIDsLength ) );
395                    
396                    // Loop on the UUIDs if any
397                    if ( syncUUIDs.size() != 0 )
398                    {
399                        for ( byte[] syncUUID:syncUUIDs )
400                        {
401                            Value.encode( buffer , syncUUID );
402                        }
403                    }
404            }
405    
406            return buffer;
407        }
408        
409        
410        /**
411         * {@inheritDoc}
412         */
413        public byte[] getValue()
414        {
415            if ( value == null )
416            {
417                try
418                { 
419                    computeLength();
420                    ByteBuffer buffer = ByteBuffer.allocate( valueLength );
421                    
422                    switch ( type )
423                    {
424                        case NEW_COOKIE :
425                            // The first case : newCookie
426                            buffer.put( (byte)SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
427    
428                            // As the OCTET_STRING is absorbed by the Application tag,
429                            // we have to store the L and V separately
430                            if ( ( cookie == null ) || ( cookie.length == 0 ) )
431                            {
432                                buffer.put( ( byte ) 0 );
433                            }
434                            else
435                            {
436                                buffer.put( TLV.getBytes( cookie.length ) );
437                                buffer.put( cookie );
438                            }
439    
440                            break;
441                            
442                        case REFRESH_DELETE :
443                            // The second case : refreshDelete
444                            buffer.put( (byte)SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
445                            buffer.put( TLV.getBytes( syncInfoValueLength ) );
446    
447                            // The cookie, if any
448                            if ( cookie != null )
449                            {
450                                Value.encode( buffer, cookie );
451                            }
452                            
453                            // The refreshDone flag
454                            if ( !refreshDone )
455                            {
456                                Value.encode( buffer, refreshDone );
457                            }
458                            
459                            break;
460                            
461                        case REFRESH_PRESENT :
462                            // The third case : refreshPresent
463                            buffer.put( (byte)SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
464                            buffer.put( TLV.getBytes( syncInfoValueLength ) );
465    
466                            // The cookie, if any
467                            if ( cookie != null )
468                            {
469                                Value.encode( buffer, cookie );
470                            }
471                            
472                            // The refreshDone flag
473                            if ( !refreshDone )
474                            {
475                                Value.encode( buffer, refreshDone );
476                            }
477    
478                            break;
479                            
480                        case SYNC_ID_SET :
481                            // The last case : syncIdSet
482                            buffer.put( (byte)SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
483                            buffer.put( TLV.getBytes( syncInfoValueLength ) );
484    
485                            // The cookie, if any
486                            if ( cookie != null )
487                            {
488                                Value.encode( buffer, cookie );
489                            }
490                            
491                            // The refreshDeletes flag if not false
492                            if ( refreshDeletes )
493                            {
494                                Value.encode( buffer, refreshDeletes );
495                            }
496                            
497                            // The syncUUIDs
498                            buffer.put( UniversalTag.SET_TAG );
499                            buffer.put( TLV.getBytes( syncUUIDsLength ) );
500                            
501                            // Loop on the UUIDs if any
502                            if ( syncUUIDs.size() != 0 )
503                            {
504                                for ( byte[] syncUUID:syncUUIDs )
505                                {
506                                    Value.encode( buffer , syncUUID );
507                                }
508                            }
509                    }
510                    
511                    value = buffer.array();
512                }
513                catch ( Exception e )
514                {
515                    return null;
516                }
517            }
518            
519            return value;
520        }
521    
522        
523        /**
524         * @see Object#toString()
525         */
526        public String toString()
527        {
528            StringBuilder sb = new StringBuilder();
529            
530            sb.append( "    SyncInfoValue control :\n" );
531            sb.append( "        oid : " ).append( getOid() ).append( '\n' );
532            sb.append( "        critical : " ).append( isCritical() ).append( '\n' );
533    
534            switch ( type )
535            {
536                case NEW_COOKIE :
537                    sb.append( "        newCookie : '" ).
538                        append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
539                    break;
540                    
541                case REFRESH_DELETE :
542                    sb.append( "        refreshDelete : \n" );
543                    
544                    if ( cookie != null )
545                    {
546                        sb.append( "            cookie : '" ).
547                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
548                    }
549                    
550                    sb.append( "            refreshDone : " ).append(  refreshDone ).append( '\n' );
551                    break;
552                    
553                case REFRESH_PRESENT :
554                    sb.append( "        refreshPresent : \n" );
555                    
556                    if ( cookie != null )
557                    {
558                        sb.append( "            cookie : '" ).
559                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
560                    }
561                    
562                    sb.append( "            refreshDone : " ).append(  refreshDone ).append( '\n' );
563                    break;
564                    
565                case SYNC_ID_SET :
566                    sb.append( "        syncIdSet : \n" );
567                    
568                    if ( cookie != null )
569                    {
570                        sb.append( "            cookie : '" ).
571                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
572                    }
573                    
574                    sb.append( "            refreshDeletes : " ).append(  refreshDeletes ).append( '\n' );
575                    sb.append(  "            syncUUIDS : " );
576    
577                    if ( syncUUIDs.size() != 0 )
578                    {
579                        boolean isFirst = true;
580                        
581                        for ( byte[] syncUUID:syncUUIDs )
582                        {
583                            if ( isFirst )
584                            {
585                                isFirst = false;
586                            }
587                            else
588                            {
589                                sb.append( ", " );
590                            }
591                            
592                            sb.append( syncUUID );
593                        }
594                        
595                        sb.append( '\n' );
596                    }
597                    else
598                    {
599                        sb.append(  "empty\n" );
600                    }
601                    
602                    break;
603            }
604            
605            return sb.toString();
606        }
607    }