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      public LdapProtocolDecoder()
67      {
68          asn1Decoder = new Asn1Decoder();
69      }
70  
71  
72      /**
73       * {@inheritDoc}
74       */
75      @Override
76      public void decode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
77      {
78          @SuppressWarnings("unchecked")
79          LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer =
80              ( LdapMessageContainer<MessageDecorator<? extends Message>> )
81              session.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
82  
83          if ( session.containsAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR ) )
84          {
85              int maxPDUSize = ( Integer ) session.getAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR );
86  
87              messageContainer.setMaxPDUSize( maxPDUSize );
88          }
89  
90          List<Message> decodedMessages = new ArrayList<>();
91          ByteBuffer buf = in.buf();
92  
93          decode( buf, messageContainer, decodedMessages );
94  
95          for ( Message message : decodedMessages )
96          {
97              out.write( message );
98          }
99      }
100 
101 
102     /**
103      * Decode an incoming buffer into LDAP messages. The result can be 0, 1 or many
104      * LDAP messages, which will be stored into the array the caller has created.
105      * 
106      * @param buffer The incoming byte buffer
107      * @param messageContainer The LdapMessageContainer which will be used to store the
108      * message being decoded. If the message is not fully decoded, the ucrrent state
109      * is stored into this container
110      * @param decodedMessages The list of decoded messages
111      * @throws Exception If the decoding failed
112      */
113     private void decode( ByteBuffer buffer, LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer,
114         List<Message> decodedMessages ) throws DecoderException
115     {
116         buffer.mark();
117 
118         while ( buffer.hasRemaining() )
119         {
120             try
121             {
122                 if ( IS_DEBUG )
123                 {
124                     CODEC_LOG.debug( "Decoding the PDU : " );
125 
126                     int size = buffer.limit();
127                     int position = buffer.position();
128                     int pduLength = size - position;
129 
130                     byte[] array = new byte[pduLength];
131 
132                     System.arraycopy( buffer.array(), position, array, 0, pduLength );
133 
134                     if ( array.length == 0 )
135                     {
136                         CODEC_LOG.debug( "NULL buffer, what the HELL ???" );
137                     }
138                     else
139                     {
140                         CODEC_LOG.debug( Strings.dumpBytes( array ) );
141                     }
142                 }
143 
144                 asn1Decoder.decode( buffer, messageContainer );
145 
146                 if ( messageContainer.getState() == TLVStateEnum.PDU_DECODED )
147                 {
148                     if ( IS_DEBUG )
149                     {
150                         CODEC_LOG.debug( "Decoded LdapMessage : " + messageContainer.getMessage() );
151                     }
152 
153                     Message message = messageContainer.getMessage();
154 
155                     decodedMessages.add( message );
156 
157                     messageContainer.clean();
158                 }
159             }
160             catch ( ResponseCarryingException rce )
161             {
162                 buffer.clear();
163                 messageContainer.clean();
164                 
165                 // Transform the DecoderException message to a MessageException
166                 ResponseCarryingMessageException rcme = new ResponseCarryingMessageException( rce.getMessage(), rce );
167                 rcme.setResponse( rce.getResponse() );
168 
169                 throw rcme;
170             }
171             catch ( DecoderException de )
172             {
173                 buffer.clear();
174                 messageContainer.clean();
175 
176                 // TODO : This is certainly not the way we should handle such an exception !
177                 throw new ResponseCarryingException( de.getMessage(), de );
178             }
179         }
180     }
181 
182 
183     /**
184      * {@inheritDoc}
185      */
186     @Override
187     public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
188     {
189         // Nothing to do
190     }
191 
192 
193     /**
194      * {@inheritDoc}
195      */
196     @Override
197     public void dispose( IoSession session ) throws Exception
198     {
199         // Nothing to do
200     }
201 }