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 */
020package org.apache.directory.api.ldap.codec.protocol.mina;
021
022
023import java.nio.ByteBuffer;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.directory.api.asn1.DecoderException;
028import org.apache.directory.api.asn1.ber.Asn1Decoder;
029import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
030import org.apache.directory.api.ldap.codec.api.LdapDecoder;
031import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
032import org.apache.directory.api.ldap.codec.api.MessageDecorator;
033import org.apache.directory.api.ldap.codec.api.ResponseCarryingException;
034import org.apache.directory.api.ldap.model.constants.Loggers;
035import org.apache.directory.api.ldap.model.exception.ResponseCarryingMessageException;
036import org.apache.directory.api.ldap.model.message.Message;
037import org.apache.directory.api.util.Strings;
038import org.apache.mina.core.buffer.IoBuffer;
039import org.apache.mina.core.session.IoSession;
040import org.apache.mina.filter.codec.ProtocolDecoder;
041import org.apache.mina.filter.codec.ProtocolDecoderOutput;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045
046/**
047 * A LDAP message decoder. It is based on api-ldap decoder.
048 *
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public class LdapProtocolDecoder implements ProtocolDecoder
052{
053    /** The logger */
054    private static final Logger CODEC_LOG = LoggerFactory.getLogger( Loggers.CODEC_LOG.getName() );
055
056    /** A speedup for logger */
057    private static final boolean IS_DEBUG = CODEC_LOG.isDebugEnabled();
058
059    /** The ASN 1 decoder instance */
060    private Asn1Decoder asn1Decoder;
061
062
063    /**
064     * Creates a new instance of LdapProtocolEncoder.
065     *
066     * @param codec The LDAP codec service associated with this encoder.
067     */
068    public LdapProtocolDecoder()
069    {
070        asn1Decoder = new Asn1Decoder();
071    }
072
073
074    /**
075     * {@inheritDoc}
076     */
077    public void decode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
078    {
079        @SuppressWarnings("unchecked")
080        LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer =
081            ( LdapMessageContainer<MessageDecorator<? extends Message>> )
082            session.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
083
084        if ( session.containsAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR ) )
085        {
086            int maxPDUSize = ( Integer ) session.getAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR );
087
088            messageContainer.setMaxPDUSize( maxPDUSize );
089        }
090
091        List<Message> decodedMessages = new ArrayList<Message>();
092        ByteBuffer buf = in.buf();
093
094        decode( buf, messageContainer, decodedMessages );
095
096        for ( Message message : decodedMessages )
097        {
098            out.write( message );
099        }
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}