View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.ldap.codec.protocol.mina;
21  
22  
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.directory.api.asn1.DecoderException;
28  import org.apache.directory.api.asn1.ber.Asn1Decoder;
29  import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
30  import org.apache.directory.api.ldap.codec.api.LdapDecoder;
31  import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
32  import org.apache.directory.api.ldap.codec.api.MessageDecorator;
33  import org.apache.directory.api.ldap.codec.api.ResponseCarryingException;
34  import org.apache.directory.api.ldap.model.constants.Loggers;
35  import org.apache.directory.api.ldap.model.exception.ResponseCarryingMessageException;
36  import org.apache.directory.api.ldap.model.message.Message;
37  import org.apache.directory.api.util.Strings;
38  import org.apache.mina.core.buffer.IoBuffer;
39  import org.apache.mina.core.session.IoSession;
40  import org.apache.mina.filter.codec.ProtocolDecoder;
41  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  
46  /**
47   * A LDAP message decoder. It is based on api-ldap decoder.
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   */
51  public class LdapProtocolDecoder implements ProtocolDecoder
52  {
53      /** The logger */
54      private static final Logger CODEC_LOG = LoggerFactory.getLogger( Loggers.CODEC_LOG.getName() );
55  
56      /** A speedup for logger */
57      private static final boolean IS_DEBUG = CODEC_LOG.isDebugEnabled();
58  
59      /** The ASN 1 decoder instance */
60      private Asn1Decoder asn1Decoder;
61  
62  
63      /**
64       * Creates a new instance of LdapProtocolEncoder.
65       *
66       * @param codec The LDAP codec service associated with this encoder.
67       */
68      public LdapProtocolDecoder()
69      {
70          asn1Decoder = new Asn1Decoder();
71      }
72  
73  
74      /**
75       * {@inheritDoc}
76       */
77      public void decode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
78      {
79          @SuppressWarnings("unchecked")
80          LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer =
81              ( LdapMessageContainer<MessageDecorator<? extends Message>> )
82              session.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
83  
84          if ( session.containsAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR ) )
85          {
86              int maxPDUSize = ( Integer ) session.getAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR );
87  
88              messageContainer.setMaxPDUSize( maxPDUSize );
89          }
90  
91          List<Message> decodedMessages = new ArrayList<Message>();
92          ByteBuffer buf = in.buf();
93  
94          decode( buf, messageContainer, decodedMessages );
95  
96          for ( Message message : decodedMessages )
97          {
98              out.write( message );
99          }
100     }
101 
102 
103     /**
104      * Decode an incoming buffer into LDAP messages. The result can be 0, 1 or many
105      * LDAP messages, which will be stored into the array the caller has created.
106      * 
107      * @param buffer The incoming byte buffer
108      * @param messageContainer The LdapMessageContainer which will be used to store the
109      * message being decoded. If the message is not fully decoded, the ucrrent state
110      * is stored into this container
111      * @param decodedMessages The list of decoded messages
112      * @throws Exception If the decoding failed
113      */
114     private void decode( ByteBuffer buffer, LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer,
115         List<Message> decodedMessages ) throws DecoderException
116     {
117         buffer.mark();
118 
119         while ( buffer.hasRemaining() )
120         {
121             try
122             {
123                 if ( IS_DEBUG )
124                 {
125                     CODEC_LOG.debug( "Decoding the PDU : " );
126 
127                     int size = buffer.limit();
128                     int position = buffer.position();
129                     int pduLength = size - position;
130 
131                     byte[] array = new byte[pduLength];
132 
133                     System.arraycopy( buffer.array(), position, array, 0, pduLength );
134 
135                     if ( array.length == 0 )
136                     {
137                         CODEC_LOG.debug( "NULL buffer, what the HELL ???" );
138                     }
139                     else
140                     {
141                         CODEC_LOG.debug( Strings.dumpBytes( array ) );
142                     }
143                 }
144 
145                 asn1Decoder.decode( buffer, messageContainer );
146 
147                 if ( messageContainer.getState() == TLVStateEnum.PDU_DECODED )
148                 {
149                     if ( IS_DEBUG )
150                     {
151                         CODEC_LOG.debug( "Decoded LdapMessage : " + messageContainer.getMessage() );
152                     }
153 
154                     Message message = messageContainer.getMessage();
155 
156                     decodedMessages.add( message );
157 
158                     messageContainer.clean();
159                 }
160             }
161             catch ( DecoderException de )
162             {
163                 buffer.clear();
164                 messageContainer.clean();
165 
166                 if ( de instanceof ResponseCarryingException )
167                 {
168                     // Transform the DecoderException message to a MessageException
169                     ResponseCarryingMessageException rcme = new ResponseCarryingMessageException( de.getMessage() );
170                     rcme.setResponse( ( ( ResponseCarryingException ) de ).getResponse() );
171 
172                     throw rcme;
173                 }
174                 else
175                 {
176                     // TODO : This is certainly not the way we should handle such an exception !
177                     throw new ResponseCarryingException( de.getMessage() );
178                 }
179             }
180         }
181     }
182 
183 
184     /**
185      * {@inheritDoc}
186      */
187     public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
188     {
189         // Nothing to do
190     }
191 
192 
193     /**
194      * {@inheritDoc}
195      */
196     public void dispose( IoSession session ) throws Exception
197     {
198         // Nothing to do
199     }
200 }