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.InputStream;
024    import java.nio.ByteBuffer;
025    
026    import org.apache.directory.shared.asn1.ber.Asn1Decoder;
027    import org.apache.directory.shared.asn1.ber.tlv.TLVStateEnum;
028    import org.apache.directory.shared.asn1.codec.DecoderException;
029    import org.apache.directory.shared.asn1.codec.stateful.DecoderCallback;
030    import org.apache.directory.shared.asn1.codec.stateful.DecoderMonitor;
031    import org.apache.directory.shared.i18n.I18n;
032    import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
033    import org.apache.directory.shared.ldap.message.spi.Provider;
034    import org.apache.directory.shared.ldap.message.spi.ProviderDecoder;
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     * The LdapDecoder decodes ASN.1 BER encoded PDUs.
043     * 
044     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045     * @version $Rev: 912399 $, $Date: 2010-02-21 21:52:31 +0100 (Sun, 21 Feb 2010) $, 
046     */
047    public class LdapDecoder implements ProviderDecoder
048    {
049        /** The logger */
050        private static Logger log = LoggerFactory.getLogger( LdapDecoder.class );
051    
052        /** A speedup for logger */
053        private static final boolean IS_DEBUG = log.isDebugEnabled();
054    
055        /** The associated Provider */
056        private final Provider provider;
057    
058        /** The message container for this instance */
059        private final LdapMessageContainer ldapMessageContainer;
060    
061        /** The Ldap BDER decoder instance */
062        private final Asn1Decoder ldapDecoder;
063    
064        /** The callback to call when the decoding is done */
065        private DecoderCallback decoderCallback;
066    
067    
068        /**
069         * Creates an instance of a Ldap Decoder implementation.
070         * 
071         * @param provider the owning provider.
072         * @param binaryAttributeDetector checks for binary attributes 
073         * @param maxPDUSize the maximum size a PDU can be
074         */
075        public LdapDecoder( Provider provider, BinaryAttributeDetector binaryAttributeDetector, int maxPDUSize )
076        {
077            this.provider = provider;
078            ldapMessageContainer = new LdapMessageContainer( binaryAttributeDetector );
079            ldapDecoder = new Asn1Decoder();
080            
081            ldapMessageContainer.setMaxPDUSize( maxPDUSize );
082        }
083    
084    
085        /**
086         * Decodes a PDU
087         * 
088         * @param encoded The PDU containing the LdapMessage to decode
089         * @throws DecoderException if anything goes wrong
090         */
091        public void decode( Object encoded ) throws DecoderException
092        {
093            ByteBuffer buf;
094            int position = 0;
095    
096            if ( encoded instanceof ByteBuffer )
097            {
098                buf = ( ByteBuffer ) encoded;
099            }
100            else if ( encoded instanceof byte[] )
101            {
102                buf = ByteBuffer.wrap( ( byte[] ) encoded );
103            }
104            else
105            {
106                throw new DecoderException( I18n.err( I18n.ERR_04059, encoded.getClass() ) );
107            }
108    
109            while ( buf.hasRemaining() )
110            {
111                try
112                {
113                    ldapDecoder.decode( buf, ldapMessageContainer );
114        
115                    if ( IS_DEBUG )
116                    {
117                        log.debug( "Decoding the PDU : " );
118        
119                        int size = buf.position();
120                        buf.flip();
121                        
122                        byte[] array = new byte[ size - position ];
123                        
124                        for ( int i = position; i < size; i++ )
125                        {
126                            array[ i ] = buf.get();
127                        }
128        
129                        position = size;
130                        
131                        log.debug( StringTools.dumpBytes( array ) );
132                    }
133                    
134                    if ( ldapMessageContainer.getState() == TLVStateEnum.PDU_DECODED )
135                    {
136                        if ( IS_DEBUG )
137                        {
138                            log.debug( "Decoded LdapMessage : " + ldapMessageContainer.getLdapMessage() );
139                            buf.mark();
140                        }
141        
142                        decoderCallback.decodeOccurred( null, ldapMessageContainer.getLdapMessage() );
143                        ldapMessageContainer.clean();
144                    }
145                }
146                catch ( DecoderException de )
147                {
148                    buf.clear();
149                    ldapMessageContainer.clean();
150                    throw de;
151                }
152            }
153        }
154    
155    
156        /**
157         * Feeds the bytes within the input stream to the digester to generate the
158         * resultant decoded Message.
159         * 
160         * @param in The InputStream containing the PDU to be decoded
161         * @throws ProviderException If the decoding went wrong
162         */
163        private void digest( InputStream in ) throws ProviderException
164        {
165            byte[] buf;
166    
167            try
168            {
169                int amount;
170    
171                while ( in.available() > 0 )
172                {
173                    buf = new byte[in.available()];
174    
175                    if ( ( amount = in.read( buf ) ) == -1 )
176                    {
177                        break;
178                    }
179    
180                    ldapDecoder.decode( ByteBuffer.wrap( buf, 0, amount ), ldapMessageContainer );
181                }
182            }
183            catch ( Exception e )
184            {
185                log.error( I18n.err( I18n.ERR_04060, e.getLocalizedMessage() ) );
186                ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04061 ) );
187                pe.addThrowable( e );
188                throw pe;
189            }
190        }
191    
192    
193        /**
194         * Decodes a PDU from an input stream into a Snickers compiler generated
195         * stub envelope.
196         * 
197         * @param lock Lock object used to exclusively read from the input stream
198         * @param in The input stream to read and decode PDU bytes from
199         * @return return decoded stub
200         */
201        public Object decode( Object lock, InputStream in ) throws ProviderException
202        {
203            if ( lock == null )
204            {
205                digest( in );
206    
207                if ( ldapMessageContainer.getState() == TLVStateEnum.PDU_DECODED )
208                {
209                    if ( IS_DEBUG )
210                    {
211                        log.debug( "Decoded LdapMessage : " + ldapMessageContainer.getLdapMessage() );
212                    }
213    
214                    return ldapMessageContainer.getLdapMessage();
215                }
216                else
217                {
218                    log.error( I18n.err( I18n.ERR_04062 ) );
219                    ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04061 ) );
220                    //noinspection ThrowableInstanceNeverThrown
221                    pe.addThrowable( new DecoderException( I18n.err( I18n.ERR_04063 ) ) );
222                    throw pe;
223                }
224            }
225            else
226            {
227                try
228                {
229                    // Synchronize on the input lock object to prevent concurrent
230                    // reads
231                    synchronized ( lock )
232                    {
233                        digest( in );
234    
235                        // Notify/awaken threads waiting to read from input stream
236                        lock.notifyAll();
237                    }
238                }
239                catch ( Exception e )
240                {
241                    log.error( I18n.err( I18n.ERR_04060, e.getLocalizedMessage() ) );
242                    ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04061 ) );
243                    pe.addThrowable( e );
244                    throw pe;
245                }
246    
247                if ( ldapMessageContainer.getState() == TLVStateEnum.PDU_DECODED )
248                {
249                    if ( IS_DEBUG )
250                    {
251                        log.debug( "Decoded LdapMessage : " + ldapMessageContainer.getLdapMessage() );
252                    }
253    
254                    return ldapMessageContainer.getLdapMessage();
255                }
256                else
257                {
258                    log.error( I18n.err( I18n.ERR_04064 ) );
259                    ProviderException pe = new ProviderException( provider, I18n.err( I18n.ERR_04062 ) );
260                    //noinspection ThrowableInstanceNeverThrown
261                    pe.addThrowable( new DecoderException( I18n.err( I18n.ERR_04063 ) ) );
262                    throw pe;
263                }
264            }
265        }
266    
267    
268        /**
269         * Gets the Provider that this Decoder implementation is part of.
270         * 
271         * @return the owning provider.
272         */
273        public Provider getProvider()
274        {
275            return provider;
276        }
277    
278    
279        /**
280         * Not used ...
281         * 
282         * @deprecated
283         */
284        public void setDecoderMonitor( DecoderMonitor monitor )
285        {
286        }
287    
288    
289        /**
290         * Set the callback to call when the PDU has been decoded
291         * 
292         * @param cb The callback
293         */
294        public void setCallback( DecoderCallback cb )
295        {
296            decoderCallback = cb;
297        }
298    }