Package org.apache.mina.filter.codec.demux

Source Code of org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory$ProtocolDecoderImpl

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.apache.mina.filter.codec.demux;

import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderException;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderException;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.util.IdentityHashSet;

/**
* A composite {@link ProtocolCodecFactory} that consists of multiple
* {@link MessageEncoder}s and {@link MessageDecoder}s.
* {@link ProtocolEncoder} and {@link ProtocolDecoder} this factory
* returns demultiplex incoming messages and buffers to
* appropriate {@link MessageEncoder}s and {@link MessageDecoder}s.
*
* <h2>Disposing resources acquired by {@link MessageEncoder} and {@link MessageDecoder}</h2>
* <p>
* Make your {@link MessageEncoder} and {@link MessageDecoder} to put all
* resources that need to be released as a session attribute.  {@link #disposeCodecResources(IoSession)}
* method will be invoked when a session is closed.  Override {@link #disposeCodecResources(IoSession)}
* to release the resources you've put as an attribute.
* <p>
* We didn't provide any <tt>dispose</tt> method for {@link MessageEncoder} and {@link MessageDecoder}
* because they can give you a big performance penalty in case you have a lot of
* message types to handle.
*
* @author The Apache Directory Project (mina-dev@directory.apache.org)
* @version $Rev: 525038 $, $Date: 2007-04-03 14:25:09 +0900 (화, 03  4월 2007) $
*
* @see MessageEncoder
* @see MessageDecoder
*/
public class DemuxingProtocolCodecFactory implements ProtocolCodecFactory
{
    private MessageDecoderFactory[] decoderFactories = new MessageDecoderFactory[0];
    private MessageEncoderFactory[] encoderFactories = new MessageEncoderFactory[0];
   
    private static final Class[] EMPTY_PARAMS = new Class[0];

    public DemuxingProtocolCodecFactory()
    {
    }
   
    public void register( Class<?> encoderOrDecoderClass )
    {
        if( encoderOrDecoderClass == null )
        {
            throw new NullPointerException( "encoderOrDecoderClass" );
        }
       
        try
        {
            encoderOrDecoderClass.getConstructor( EMPTY_PARAMS );
        }
        catch( NoSuchMethodException e )
        {
            throw new IllegalArgumentException( "The specifiec class doesn't have a public default constructor." );
        }

        boolean registered = false;
        if( MessageEncoder.class.isAssignableFrom( encoderOrDecoderClass ) )
        {
            register( new DefaultConstructorMessageEncoderFactory( encoderOrDecoderClass ) );
            registered = true;
        }
       
        if( MessageDecoder.class.isAssignableFrom( encoderOrDecoderClass ) )
        {
            register( new DefaultConstructorMessageDecoderFactory( encoderOrDecoderClass ) );
            registered = true;
        }
       
        if( !registered )
        {
            throw new IllegalArgumentException( "Unregisterable type: " + encoderOrDecoderClass );
        }
    }

   
    public void register( MessageEncoder encoder )
    {
        register( new SingletonMessageEncoderFactory( encoder ) );
    }
   
    public void register( MessageEncoderFactory factory )
    {
        if( factory == null )
        {
            throw new NullPointerException( "factory" );
        }
        MessageEncoderFactory[] encoderFactories = this.encoderFactories;
        MessageEncoderFactory[] newEncoderFactories = new MessageEncoderFactory[ encoderFactories.length + 1 ];
        System.arraycopy( encoderFactories, 0, newEncoderFactories, 0, encoderFactories.length );
        newEncoderFactories[ encoderFactories.length ] = factory;
        this.encoderFactories = newEncoderFactories;
    }
   
    public void register( MessageDecoder decoder )
    {
        register( new SingletonMessageDecoderFactory( decoder ) );
    }
   
    public void register( MessageDecoderFactory factory )
    {
        if( factory == null )
        {
            throw new NullPointerException( "factory" );
        }
        MessageDecoderFactory[] decoderFactories = this.decoderFactories;
        MessageDecoderFactory[] newDecoderFactories = new MessageDecoderFactory[ decoderFactories.length + 1 ];
        System.arraycopy( decoderFactories, 0, newDecoderFactories, 0, decoderFactories.length );
        newDecoderFactories[ decoderFactories.length ] = factory;
        this.decoderFactories = newDecoderFactories;
    }
   
    public ProtocolEncoder getEncoder() throws Exception
    {
        return new ProtocolEncoderImpl();
    }

    public ProtocolDecoder getDecoder() throws Exception
    {
        return new ProtocolDecoderImpl();
    }
   
    /**
     * Implement this method to release all resources acquired to perform
     * encoding and decoding messages for the specified <tt>session</tt>.
     * By default, this method does nothing.
     *
     * @param session the session that requires resource deallocation now
     */
    protected void disposeCodecResources( IoSession session )
    {
        // Do nothing by default; let users implement it as they want.
       
        // This statement is just to avoid compiler warning.  Please ignore.
        session.getTransportType();
    }
   
    private class ProtocolEncoderImpl implements ProtocolEncoder
    {
        private final Map<Class, MessageEncoder> encoders = new IdentityHashMap<Class, MessageEncoder>();
       
        private ProtocolEncoderImpl() throws Exception
        {
            MessageEncoderFactory[] encoderFactories = DemuxingProtocolCodecFactory.this.encoderFactories;
            for( int i = encoderFactories.length - 1; i >= 0; i-- )
            {
                MessageEncoder encoder = encoderFactories[ i ].getEncoder();
                Set<Class> messageTypes = encoder.getMessageTypes();
                if (messageTypes == null) {
                    throw new IllegalStateException(
                            encoder.getClass().getName() + "#getMessageTypes() may not return null.");
                }
               
                Iterator it = messageTypes.iterator();
                while( it.hasNext() )
                {
                    Class type = ( Class ) it.next();
                    encoders.put( type, encoder );
                }
            }
        }
       
        public void encode( IoSession session, Object message,
                            ProtocolEncoderOutput out ) throws Exception
        {
            Class type = message.getClass();
            MessageEncoder encoder = findEncoder( type );
            if( encoder == null )
            {
                throw new ProtocolEncoderException( "Unexpected message type: " + type );
            }
           
            encoder.encode( session, message, out );
        }
       
        private MessageEncoder findEncoder( Class type )
        {
            MessageEncoder encoder = encoders.get( type );
            if( encoder == null )
            {
                encoder = findEncoder( type, new IdentityHashSet<Class>() );
            }

            return encoder;
        }

        private MessageEncoder findEncoder( Class type, Set<Class> triedClasses )
        {
            MessageEncoder encoder;

            if( triedClasses.contains( type ) )
                return null;
            triedClasses.add( type );

            encoder = encoders.get( type );
            if( encoder == null )
            {
                encoder = findEncoder( type, triedClasses );
                if( encoder != null )
                    return encoder;

                Class[] interfaces = type.getInterfaces();
                for( int i = 0; i < interfaces.length; i ++ )
                {
                    encoder = findEncoder( interfaces[ i ], triedClasses );
                    if( encoder != null )
                        return encoder;
                }

                return null;
            }
            else
                return encoder;
        }

        public void dispose( IoSession session ) throws Exception
        {
            DemuxingProtocolCodecFactory.this.disposeCodecResources( session );
        }
    }
   
    private class ProtocolDecoderImpl extends CumulativeProtocolDecoder
    {
        private final MessageDecoder[] decoders;
        private MessageDecoder currentDecoder;

        protected ProtocolDecoderImpl() throws Exception
        {
            MessageDecoderFactory[] decoderFactories = DemuxingProtocolCodecFactory.this.decoderFactories;
            decoders = new MessageDecoder[ decoderFactories.length ];
            for( int i = decoderFactories.length - 1; i >= 0; i-- )
            {
                decoders[ i ] = decoderFactories[ i ].getDecoder();
            }
        }

        protected boolean doDecode( IoSession session, ByteBuffer in,
                                    ProtocolDecoderOutput out ) throws Exception
        {
            if( currentDecoder == null )
            {
                MessageDecoder[] decoders = this.decoders;
                int undecodables = 0;
                for( int i = decoders.length - 1; i >= 0; i -- )
                {
                    MessageDecoder decoder = decoders[i];
                    int limit = in.limit();
                    int pos = in.position();

                    MessageDecoderResult result;
                    try
                    {
                        result = decoder.decodable( session, in );
                    }
                    finally
                    {
                        in.position( pos );
                        in.limit( limit );
                    }
                   
                    if( result == MessageDecoder.OK )
                    {
                        currentDecoder = decoder;
                        break;
                    }
                    else if( result == MessageDecoder.NOT_OK )
                    {
                        undecodables ++;
                    }
                    else if( result != MessageDecoder.NEED_DATA )
                    {
                        throw new IllegalStateException( "Unexpected decode result (see your decodable()): " + result );
                    }
                }
               
                if( undecodables == decoders.length )
                {
                    // Throw an exception if all decoders cannot decode data.
                    String dump = in.getHexDump();
                    in.position( in.limit() ); // Skip data
                    throw new ProtocolDecoderException(
                            "No appropriate message decoder: " + dump );
                }
               
                if( currentDecoder == null )
                {
                    // Decoder is not determined yet (i.e. we need more data)
                    return false;
                }
            }
           
            MessageDecoderResult result = currentDecoder.decode( session, in, out );
            if( result == MessageDecoder.OK )
            {
                currentDecoder = null;
                return true;
            }
            else if( result == MessageDecoder.NEED_DATA )
            {
                return false;
            }
            else if( result == MessageDecoder.NOT_OK )
            {
                throw new ProtocolDecoderException( "Message decoder returned NOT_OK." );
            }
            else
            {
                throw new IllegalStateException( "Unexpected decode result (see your decode()): " + result );
            }
        }

        public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
        {
            if( currentDecoder == null )
            {
                return;
            }
           
            currentDecoder.finishDecode( session, out );
        }

        public void dispose( IoSession session ) throws Exception
        {
            super.dispose( session );
           
            // ProtocolEncoder.dispose() already called disposeCodec(),
            // so there's nothing more we need to do.
        }
    }
   
    private static class SingletonMessageEncoderFactory implements MessageEncoderFactory
    {
        private final MessageEncoder encoder;
       
        private SingletonMessageEncoderFactory( MessageEncoder encoder )
        {
            if( encoder == null )
            {
                throw new NullPointerException( "encoder" );
            }
            this.encoder = encoder;
        }

        public MessageEncoder getEncoder()
        {
            return encoder;
        }
    }

    private static class SingletonMessageDecoderFactory implements MessageDecoderFactory
    {
        private final MessageDecoder decoder;
       
        private SingletonMessageDecoderFactory( MessageDecoder decoder )
        {
            if( decoder == null )
            {
                throw new NullPointerException( "decoder" );
            }
            this.decoder = decoder;
        }

        public MessageDecoder getDecoder()
        {
            return decoder;
        }
    }

   
    private static class DefaultConstructorMessageEncoderFactory implements MessageEncoderFactory
    {
        private final Class encoderClass;
       
        private DefaultConstructorMessageEncoderFactory( Class encoderClass )
        {
            if( encoderClass == null )
            {
                throw new NullPointerException( "encoderClass" );
            }
           
            if( !MessageEncoder.class.isAssignableFrom( encoderClass ) )
            {
                throw new IllegalArgumentException( "encoderClass is not assignable to MessageEncoder" );
            }
            this.encoderClass = encoderClass;
        }

        public MessageEncoder getEncoder() throws Exception
        {
            return ( MessageEncoder ) encoderClass.newInstance();
        }
    }

    private static class DefaultConstructorMessageDecoderFactory implements MessageDecoderFactory
    {
        private final Class decoderClass;
       
        private DefaultConstructorMessageDecoderFactory( Class decoderClass )
        {
            if( decoderClass == null )
            {
                throw new NullPointerException( "decoderClass" );
            }
           
            if( !MessageDecoder.class.isAssignableFrom( decoderClass ) )
            {
                throw new IllegalArgumentException( "decoderClass is not assignable to MessageDecoder" );
            }
            this.decoderClass = decoderClass;
        }

        public MessageDecoder getDecoder() throws Exception
        {
            return ( MessageDecoder ) decoderClass.newInstance();
        }
    }
}
TOP

Related Classes of org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory$ProtocolDecoderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.