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.ldap.message.spi.BinaryAttributeDetector;
032 import org.apache.directory.shared.ldap.message.spi.Provider;
033 import org.apache.directory.shared.ldap.message.spi.ProviderDecoder;
034 import org.apache.directory.shared.ldap.message.spi.ProviderException;
035 import org.apache.directory.shared.ldap.util.StringTools;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038
039
040 /**
041 * The TwixDecoder decodes ASN.1 BER encoded PDUs.
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 * @version $Rev: 725712 $, $Date: 2008-12-11 16:32:04 +0100 (Jeu, 11 déc 2008) $,
045 */
046 public class TwixDecoder implements ProviderDecoder
047 {
048 /** The logger */
049 private static Logger log = LoggerFactory.getLogger( TwixDecoder.class );
050
051 /** A speedup for logger */
052 private static final boolean IS_DEBUG = log.isDebugEnabled();
053
054 /** The associated Provider */
055 private final Provider provider;
056
057 /** The message container for this instance */
058 private final LdapMessageContainer ldapMessageContainer;
059
060 /** The Ldap BDER decoder instance */
061 private final Asn1Decoder ldapDecoder;
062
063 /** The callback to call when the decoding is done */
064 private DecoderCallback decoderCallback;
065
066
067 /**
068 * Creates an instance of a Twix Decoder implementation.
069 *
070 * @param provider the owning provider.
071 * @param binaryAttributeDetector checks for binary attributes
072 * @param maxPDUSize the maximum size a PDU can be
073 */
074 public TwixDecoder( Provider provider, BinaryAttributeDetector binaryAttributeDetector, int maxPDUSize )
075 {
076 this.provider = provider;
077 ldapMessageContainer = new LdapMessageContainer( binaryAttributeDetector );
078 ldapDecoder = new LdapDecoder();
079
080 ldapMessageContainer.setMaxPDUSize( maxPDUSize );
081 }
082
083
084 /**
085 * Decodes a PDU
086 *
087 * @param encoded The PDU containing the LdapMessage to decode
088 * @throws DecoderException if anything goes wrong
089 */
090 public void decode( Object encoded ) throws DecoderException
091 {
092 ByteBuffer buf;
093 int position = 0;
094
095 if ( encoded instanceof ByteBuffer )
096 {
097 buf = ( ByteBuffer ) encoded;
098 }
099 else if ( encoded instanceof byte[] )
100 {
101 buf = ByteBuffer.wrap( ( byte[] ) encoded );
102 }
103 else
104 {
105 throw new DecoderException( "Expected either a byte[] or " + "ByteBuffer argument but got a "
106 + 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( "Twix decoder failure : " + e.getMessage() );
186 ProviderException pe = new ProviderException( provider, "Twix decoder failure!" );
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( "Twix decoder failure, PDU does not contain enough data" );
219 ProviderException pe = new ProviderException( provider, "Twix decoder failure!" );
220 //noinspection ThrowableInstanceNeverThrown
221 pe.addThrowable( new DecoderException( "The input stream does not contain a full PDU" ) );
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( "Twix decoder failure : " + e.getMessage() );
242 ProviderException pe = new ProviderException( provider, "Twix decoder failure!" );
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( "Twix decoder failure : The input stream does not contain a full PDU" );
259 ProviderException pe = new ProviderException( provider, "Twix decoder failure!" );
260 //noinspection ThrowableInstanceNeverThrown
261 pe.addThrowable( new DecoderException( "The input stream does not contain a full PDU" ) );
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 }