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 21:52:31 +0100 (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    }