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    public LdapProtocolDecoder()
067    {
068        asn1Decoder = new Asn1Decoder();
069    }
070
071
072    /**
073     * {@inheritDoc}
074     */
075    @Override
076    public void decode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
077    {
078        @SuppressWarnings("unchecked")
079        LdapMessageContainer<MessageDecorator<? extends Message>> messageContainer =
080            ( LdapMessageContainer<MessageDecorator<? extends Message>> )
081            session.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
082
083        if ( session.containsAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR ) )
084        {
085            int maxPDUSize = ( Integer ) session.getAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR );
086
087            messageContainer.setMaxPDUSize( maxPDUSize );
088        }
089
090        List<Message> decodedMessages = new ArrayList<>();
091        ByteBuffer buf = in.buf();
092
093        decode( buf, messageContainer, decodedMessages );
094
095        for ( Message message : decodedMessages )
096        {
097            out.write( message );
098        }
099    }
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}