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.io.IOException;
024    import java.io.OutputStream;
025    import java.nio.ByteBuffer;
026    import java.nio.channels.Channels;
027    import java.nio.channels.WritableByteChannel;
028    
029    import org.apache.directory.shared.asn1.codec.EncoderException;
030    import org.apache.directory.shared.asn1.codec.stateful.EncoderCallback;
031    import org.apache.directory.shared.asn1.codec.stateful.EncoderMonitor;
032    import org.apache.directory.shared.asn1.codec.stateful.StatefulEncoder;
033    import org.apache.directory.shared.i18n.I18n;
034    import org.apache.directory.shared.ldap.codec.add.AddRequestCodec;
035    import org.apache.directory.shared.ldap.message.spi.Provider;
036    import org.apache.directory.shared.ldap.message.spi.ProviderEncoder;
037    import org.apache.directory.shared.ldap.message.spi.ProviderException;
038    import org.apache.directory.shared.ldap.util.StringTools;
039    import org.slf4j.Logger;
040    import org.slf4j.LoggerFactory;
041    
042    
043    /**
044     * LDAP BER provider's encoder.
045     * 
046     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047     * @version $Rev: 912399 $
048     */
049    public class LdapEncoder implements ProviderEncoder
050    {
051        //TM private static long cumul = 0L;
052        //TM private static long count = 0L;
053        //TM private Object lock = new Object();
054    
055        /** The logger */
056        private static Logger log = LoggerFactory.getLogger( LdapEncoder.class );
057    
058        /** A speedup for logger */
059        private static final boolean IS_DEBUG = log.isDebugEnabled();
060        
061        /** The associated Provider */
062        final Provider provider;
063    
064        /** The callback to call when the encoding is done */
065        private EncoderCallback encodeCallback;
066    
067    
068        /**
069         * Creates an instance of a Ldap Encoder implementation.
070         * 
071         * @param provider The associated Provider
072         */
073        public LdapEncoder( Provider provider )
074        {
075            this.provider = provider;
076            encodeCallback = new OutputCallback();
077        }
078    
079    
080        /**
081         * Encodes a LdapMessage, and calls the callback.
082         * 
083         * @param lock Not used...
084         * @param out Not used ...
085         * @param obj The LdapMessage to encode
086         * @throws ProviderException If anything went wrong
087         */
088        public void encodeBlocking( Object lock, OutputStream out, Object obj ) throws ProviderException
089        {
090            try
091            {
092                if ( IS_DEBUG )
093                {
094                    log.debug( "Encoding this LdapMessage : " + obj );
095                }
096    
097                ( ( OutputCallback ) encodeCallback ).attach( out );
098                encodeCallback.encodeOccurred( null, ( ( LdapMessageCodec ) obj ).encode() );
099            }
100            catch ( EncoderException e )
101            {
102                log.error( I18n.err( I18n.ERR_04065, obj, e.getLocalizedMessage() ) );
103                ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04065, obj, e.getLocalizedMessage() ) );
104                throw pe;
105            }
106        }
107    
108    
109        /**
110         * Encodes a LdapMessage, and return a ByteBuffer containing the resulting
111         * PDU
112         * 
113         * @param obj The LdapMessage to encode
114         * @return The ByteBuffer containing the PDU
115         * @throws ProviderException If anything went wrong
116         */
117        public ByteBuffer encodeBlocking( Object obj ) throws ProviderException
118        {
119            try
120            {
121                if ( IS_DEBUG )
122                {
123                    log.debug( "Encoding this LdapMessage : " + obj );
124                }
125    
126                ByteBuffer pdu = ( ( LdapMessageCodec ) obj ).encode();
127    
128                if ( IS_DEBUG )
129                {
130                    log.debug( "Encoded PDU : " + StringTools.dumpBytes( pdu.array() ) );
131                }
132    
133                pdu.flip();
134                return pdu;
135            }
136            catch ( EncoderException e )
137            {
138                log.error( I18n.err( I18n.ERR_04065, obj, e.getLocalizedMessage() ) );
139                ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04065, obj, e.getLocalizedMessage() ) );
140                throw pe;
141            }
142        }
143    
144    
145        /**
146         * Encodes a LdapMessage, and return a byte array containing the resulting
147         * PDU
148         * 
149         * @param obj The LdapMessage to encode
150         * @return The byte[] containing the PDU
151         * @throws ProviderException If anything went wrong
152         */
153        public byte[] encodeToArray( Object obj ) throws ProviderException
154        {
155            try
156            {
157                if ( IS_DEBUG )
158                {
159                    log.debug( "Encoding this LdapMessage : " + obj );
160                }
161    
162                byte[] pdu = ( ( LdapMessageCodec ) obj ).encode().array();
163    
164                if ( IS_DEBUG )
165                {
166                    log.debug( "Encoded PDU : " + StringTools.dumpBytes( pdu ) );
167                }
168    
169                return pdu;
170            }
171            catch ( EncoderException e )
172            {
173                log.error( I18n.err( I18n.ERR_04065,  obj, e.getLocalizedMessage() ) );
174                ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04065, obj, e.getLocalizedMessage() ) );
175                throw pe;
176            }
177        }
178    
179    
180        /**
181         * Gets the Provider associated with this SPI implementation object.
182         * 
183         * @return Provider The provider
184         */
185        public Provider getProvider()
186        {
187            return provider;
188        }
189    
190    
191        /**
192         * Encodes a LdapMessage, and calls the callback
193         * 
194         * @param obj The LdapMessage to encode
195         * @throws EncoderException If anything went wrong
196         */
197        public void encode( Object obj ) throws EncoderException
198        {
199            //TM long t0 = System.nanoTime();
200            ByteBuffer encoded = encodeBlocking( obj );
201            encodeCallback.encodeOccurred( null, encoded );
202            //TM long t1 = System.nanoTime();
203            
204            //TM synchronized (lock)
205            //TM {
206            //TM     cumul += (t1 - t0);
207            //TM     count++;
208            //TM    
209            //TM
210            //TM     if ( count % 1000L == 0)
211            //TM     {
212            //TM         System.out.println( "Encode cost : " + (cumul/count) );
213            //TM         cumul = 0L;
214            //TM     }
215            //TM }
216        }
217    
218    
219        /**
220         * Set the callback called when the encoding is done.
221         * 
222         * @param cb The callback.
223         */
224        public void setCallback( EncoderCallback cb )
225        {
226            encodeCallback = cb;
227        }
228    
229    
230        /**
231         * Not used ...
232         * 
233         * @deprecated
234         */
235        public void setEncoderMonitor( EncoderMonitor monitor )
236        {
237        }
238    
239        /**
240         * The inner class used to write the PDU to a channel.
241         */
242        class OutputCallback implements EncoderCallback
243        {
244            /** The channel in which the PDU will be written */
245            private WritableByteChannel channel = null;
246    
247    
248            /**
249             * Callback to deliver a fully encoded object.
250             * 
251             * @param encoder the stateful encoder driving the callback
252             * @param encoded the object that was encoded
253             */
254            public void encodeOccurred( StatefulEncoder encoder, Object encoded )
255            {
256                try
257                {
258                    ( ( ByteBuffer ) encoded ).flip();
259                    channel.write( ( ByteBuffer ) encoded );
260                }
261                catch ( IOException e )
262                {
263                    ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04065, "", e.getLocalizedMessage() ) );
264                    throw pe;
265                }
266            }
267    
268    
269            /**
270             * Associate a channel to the callback
271             * 
272             * @param channel The channel to use to write a PDU
273             */
274            void attach( WritableByteChannel channel )
275            {
276                this.channel = channel;
277            }
278    
279    
280            /**
281             * Associate a OutputStream to the callback. A channel will be created.
282             * 
283             * @param out The OutputStream to use
284             */
285            void attach( OutputStream out )
286            {
287                this.channel = Channels.newChannel( out );
288            }
289        }
290        
291        
292        private static ByteBuffer encodeAsn1( AddRequestCodec addRequest )
293        {
294            //int length = computeLength( addRequest );
295            return null;
296        }
297        
298        
299        private static ByteBuffer encodeAsn1( LdapMessageCodec message ) throws EncoderException
300        {
301            ByteBuffer buffer = null;
302            
303            switch ( message.getMessageType() )
304            {
305                case ABANDON_REQUEST :
306                    return encodeAsn1( message );
307                    
308                case ADD_REQUEST :
309                case ADD_RESPONSE :
310                case BIND_REQUEST :
311                case BIND_RESPONSE :
312                case COMPARE_REQUEST :
313                case COMPARE_RESPONSE :
314                case DEL_REQUEST :
315                case DEL_RESPONSE :
316                case EXTENDED_REQUEST :
317                case EXTENDED_RESPONSE :
318                case INTERMEDIATE_RESPONSE :
319                case MODIFY_REQUEST :
320                case MODIFY_RESPONSE :
321                case MODIFYDN_REQUEST :
322                case MODIFYDN_RESPONSE :
323                case SEARCH_REQUEST :
324                case SEARCH_RESULT_DONE :
325                case SEARCH_RESULT_ENTRY :
326                case SEARCH_RESULT_REFERENCE  :
327                case UNBIND_REQUEST :
328                
329            }
330            
331            return buffer;
332        }
333    }