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.ldap.message.spi.Provider;
034    import org.apache.directory.shared.ldap.message.spi.ProviderEncoder;
035    import org.apache.directory.shared.ldap.message.spi.ProviderException;
036    import org.apache.directory.shared.ldap.util.StringTools;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    
041    /**
042     * Twix LDAP BER provider's encoder.
043     * 
044     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045     * @version $Rev: 764131 $
046     */
047    public class TwixEncoder implements ProviderEncoder
048    {
049        //TM private static long cumul = 0L;
050        //TM private static long count = 0L;
051        //TM private Object lock = new Object();
052    
053        /** The logger */
054        private static Logger log = LoggerFactory.getLogger( TwixEncoder.class );
055    
056        /** A speedup for logger */
057        private static final boolean IS_DEBUG = log.isDebugEnabled();
058        
059        /** The associated Provider */
060        final Provider provider;
061    
062        /** The callback to call when the encoding is done */
063        private EncoderCallback encodeCallback;
064    
065    
066        /**
067         * Creates an instance of a Twix Encoder implementation.
068         * 
069         * @param provider The associated Provider
070         */
071        public TwixEncoder( Provider provider )
072        {
073            this.provider = provider;
074            encodeCallback = new OutputCallback();
075        }
076    
077    
078        /**
079         * Encodes a LdapMessage, and calls the callback.
080         * 
081         * @param lock Not used...
082         * @param out Not used ...
083         * @param obj The LdapMessage to encode
084         * @throws ProviderException If anything went wrong
085         */
086        public void encodeBlocking( Object lock, OutputStream out, Object obj ) throws ProviderException
087        {
088            try
089            {
090                if ( IS_DEBUG )
091                {
092                    log.debug( "Encoding this LdapMessage : " + obj );
093                }
094    
095                ( ( OutputCallback ) encodeCallback ).attach( out );
096                encodeCallback.encodeOccurred( null, ( ( LdapMessageCodec ) obj ).encode( null ) );
097            }
098            catch ( EncoderException e )
099            {
100                log.error( "Twix encoder failed to encode object: " + obj + ", error : " + e.getMessage() );
101                ProviderException pe = new ProviderException( provider, "Twix encoder failed to encode object: " + obj
102                    + ", error : " + e.getMessage() );
103                throw pe;
104            }
105        }
106    
107    
108        /**
109         * Encodes a LdapMessage, and return a ByteBuffer containing the resulting
110         * PDU
111         * 
112         * @param obj The LdapMessage to encode
113         * @return The ByteBuffer containing the PDU
114         * @throws ProviderException If anything went wrong
115         */
116        public ByteBuffer encodeBlocking( Object obj ) throws ProviderException
117        {
118            try
119            {
120                if ( IS_DEBUG )
121                {
122                    log.debug( "Encoding this LdapMessage : " + obj );
123                }
124    
125                ByteBuffer pdu = ( ( LdapMessageCodec ) obj ).encode( null );
126    
127                if ( IS_DEBUG )
128                {
129                    log.debug( "Encoded PDU : " + StringTools.dumpBytes( pdu.array() ) );
130                }
131    
132                pdu.flip();
133                return pdu;
134            }
135            catch ( EncoderException e )
136            {
137                log.error( "Twix encoder failed to encode object: " + obj + ", error : " + e.getMessage() );
138                ProviderException pe = new ProviderException( provider, "Twix encoder failed to encode object: " + obj
139                    + ", error : " + e.getMessage() );
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( null ).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( "Twix encoder failed to encode object: " + obj + ", error : " + e.getMessage() );
174                ProviderException pe = new ProviderException( provider, "Twix encoder failed to encode object: " + obj
175                    + ", error : " + e.getMessage() );
176                throw pe;
177            }
178        }
179    
180    
181        /**
182         * Gets the Provider associated with this SPI implementation object.
183         * 
184         * @return Provider The provider
185         */
186        public Provider getProvider()
187        {
188            return provider;
189        }
190    
191    
192        /**
193         * Encodes a LdapMessage, and calls the callback
194         * 
195         * @param obj The LdapMessage to encode
196         * @throws EncoderException If anything went wrong
197         */
198        public void encode( Object obj ) throws EncoderException
199        {
200            //TM long t0 = System.nanoTime();
201            ByteBuffer encoded = encodeBlocking( obj );
202            encodeCallback.encodeOccurred( null, encoded );
203            //TM long t1 = System.nanoTime();
204            
205            //TM synchronized (lock)
206            //TM {
207            //TM     cumul += (t1 - t0);
208            //TM     count++;
209            //TM    
210            //TM
211            //TM     if ( count % 1000L == 0)
212            //TM     {
213            //TM         System.out.println( "Encode cost : " + (cumul/count) );
214            //TM         cumul = 0L;
215            //TM     }
216            //TM }
217        }
218    
219    
220        /**
221         * Set the callback called when the encoding is done.
222         * 
223         * @param cb The callback.
224         */
225        public void setCallback( EncoderCallback cb )
226        {
227            encodeCallback = cb;
228        }
229    
230    
231        /**
232         * Not used ...
233         * 
234         * @deprecated
235         */
236        public void setEncoderMonitor( EncoderMonitor monitor )
237        {
238        }
239    
240        /**
241         * The inner class used to write the PDU to a channel.
242         */
243        class OutputCallback implements EncoderCallback
244        {
245            /** The channel in which the PDU will be written */
246            private WritableByteChannel channel = null;
247    
248    
249            /**
250             * Callback to deliver a fully encoded object.
251             * 
252             * @param encoder the stateful encoder driving the callback
253             * @param encoded the object that was encoded
254             */
255            public void encodeOccurred( StatefulEncoder encoder, Object encoded )
256            {
257                try
258                {
259                    ( ( ByteBuffer ) encoded ).flip();
260                    channel.write( ( ByteBuffer ) encoded );
261                }
262                catch ( IOException e )
263                {
264                    ProviderException pe = new ProviderException( provider,
265                        "Twix encoder failed to encode object, error : " + e.getMessage() );
266                    throw pe;
267                }
268            }
269    
270    
271            /**
272             * Associate a channel to the callback
273             * 
274             * @param channel The channel to use to write a PDU
275             */
276            void attach( WritableByteChannel channel )
277            {
278                this.channel = channel;
279            }
280    
281    
282            /**
283             * Associate a OutputStream to the callback. A channel will be created.
284             * 
285             * @param out The OutputStream to use
286             */
287            void attach( OutputStream out )
288            {
289                this.channel = Channels.newChannel( out );
290            }
291        }
292    }