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.message;
021    
022    
023    import org.apache.directory.shared.asn1.Asn1Object;
024    import org.apache.directory.shared.asn1.codec.DecoderException;
025    import org.apache.directory.shared.asn1.codec.stateful.DecoderCallback;
026    import org.apache.directory.shared.asn1.codec.stateful.DecoderMonitor;
027    import org.apache.directory.shared.asn1.codec.stateful.StatefulDecoder;
028    import org.apache.directory.shared.ldap.codec.ResponseCarryingException;
029    import org.apache.directory.shared.ldap.codec.LdapTransformer;
030    import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
031    import org.apache.directory.shared.ldap.message.spi.Provider;
032    import org.apache.directory.shared.ldap.message.spi.ProviderDecoder;
033    
034    import java.io.InputStream;
035    import java.util.Hashtable;
036    
037    
038    /**
039     * Decodes a BER encoded LDAPv3 message envelope from an input stream
040     * demarshaling it into a Message instance using a BER library provider.
041     * 
042     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043     * @version $Rev: 903719 $
044     */
045    public final class MessageDecoder implements ProviderDecoder
046    {
047        /** the ASN.1 provider */
048        private final Provider provider;
049    
050        /** the ASN.1 provider's decoder */
051        private final ProviderDecoder decoder;
052    
053        /** the Message decode operation callback */
054        private DecoderCallback cb;
055    
056    
057        /**
058         * Creates a MessageDecoder using default properties for enabling a BER
059         * library provider.
060         * 
061         * @param binaryAttributeDetector detects whether or not an attribute is binary
062         * @throws MessageException if there is a problem creating this decoder.
063         */
064        public MessageDecoder( BinaryAttributeDetector binaryAttributeDetector ) throws MessageException
065        {
066            this( binaryAttributeDetector, Integer.MAX_VALUE );
067        }
068        
069        
070        /**
071         * Creates a MessageDecoder using default properties for enabling a BER
072         * library provider.
073         * 
074         * @param binaryAttributeDetector detects whether or not an attribute is binary
075         * @param maxPDUSize the maximum size a PDU can be
076         * @throws MessageException if there is a problem creating this decoder.
077         */
078        public MessageDecoder( BinaryAttributeDetector binaryAttributeDetector, int maxPDUSize ) throws MessageException
079        {
080            // We need to get the encoder class name
081            Hashtable<Object, Object> providerEnv = Provider.getEnvironment();
082            
083            this.provider = Provider.getProvider( providerEnv );
084            this.decoder = this.provider.getDecoder( binaryAttributeDetector, maxPDUSize );
085            this.decoder.setCallback( new DecoderCallback()
086            {
087                public void decodeOccurred( StatefulDecoder decoder, Object decoded )
088                {
089                    if ( decoded instanceof Asn1Object )
090                    {
091                        cb.decodeOccurred( decoder, LdapTransformer.transform( decoded ) );
092                    }
093                    else
094                    {
095                        cb.decodeOccurred( decoder, decoded );
096                    }
097                }
098            } );
099        }
100    
101    
102        /**
103         * Reads and decodes a BER encoded LDAPv3 ASN.1 message envelope structure
104         * from an input stream to build a fully populated Message object instance.
105         * 
106         * @param lock
107         *            lock object used to exclusively read from the input stream
108         * @param in
109         *            the input stream to read PDU data from.
110         * @return the populated Message representing the PDU envelope.
111         * @throws MessageException
112         *             if there is a problem decoding.
113         */
114        public Object decode( final Object lock, final InputStream in ) throws MessageException
115        {
116            Object providerEnvelope;
117    
118            try
119            {
120                if ( lock == null )
121                {
122                    // Complain here somehow first then do the following w/o synch!
123        
124                    // Call provider decoder to demarshall PDU into berlib specific form
125                    providerEnvelope = decoder.decode( lock, in );
126                }
127                else
128                {
129                    synchronized ( lock )
130                    {
131                        // Same as above but a synchronized read using valid lock object
132                        providerEnvelope = decoder.decode( lock, in );
133                        lock.notifyAll();
134                    }
135                }
136            }
137            catch ( Exception e )
138            {
139                throw ( MessageException ) e;
140            }
141    
142            // Call on transformer to convert stub based PDU into Message based PDU
143            return LdapTransformer.transform( providerEnvelope );
144        }
145    
146    
147        /**
148         * Decodes a chunk of stream data returning any resultant decoded PDU via a
149         * callback.
150         * 
151         * @param chunk
152         *            the chunk to decode
153         * @throws MessageException
154         *             if there are failures while decoding the chunk
155         */
156        public void decode( Object chunk ) throws MessageException
157        {
158            try
159            {
160                decoder.decode( chunk );
161            }
162            catch ( DecoderException e )
163            {
164                // transform the DecoderException message to a MessageException
165                if ( e instanceof ResponseCarryingException ) 
166                {
167                    ResponseCarryingMessageException rcme = new ResponseCarryingMessageException( e.getMessage() );
168                    rcme.setResponse( ((ResponseCarryingException)e).getResponse() );
169                    
170                    throw rcme;
171                }
172                else
173                {
174                    // TODO : this is certainly not the way we should handle such an exception !
175                    throw new ResponseCarryingMessageException( e.getMessage() );
176                }
177            }
178        }
179    
180    
181        /**
182         * Sets the callback used to deliver completly decoded PDU's.
183         * 
184         * @param cb
185         *            the callback to use for decoded PDU delivery
186         */
187        public void setCallback( DecoderCallback cb )
188        {
189            this.cb = cb;
190        }
191    
192    
193        /**
194         * Sets the monitor for this MessageDecoder which receives callbacks for
195         * noteworthy events during decoding.
196         * 
197         * @param monitor
198         *            the monitor to receive notifications via callback events
199         */
200        public void setDecoderMonitor( DecoderMonitor monitor )
201        {
202            decoder.setDecoderMonitor( monitor );
203        }
204    
205    
206        public Provider getProvider()
207        {
208            return this.provider;
209        }
210    }