Mina protocol codec filter three (session write and message reception filtering)

Mina protocol codec filter 1 (protocol codec factory, protocol encoder):
http://donald-draper.iteye.com/blog/2376663
Mina protocol codec filter 2 (protocol decoder):
http://donald-draper.iteye.com/blog/2376663 donald-draper.iteye.com/blog/2376679
Introduction:
    We have seen the related concepts involved in the protocol codec filter before, let's review it first: the
protocol codec filter is associated with a protocol codec factory, a protocol codec factory Used to create protocol encoders and decoders; the protocol encoder encodes the upper-layer message into data in a secondary or specific protocol format, writes it to the byte queue output by the protocol encoder, and flushes the data in the byte queue (filterWrite) to the next filter. The protocol decoder decodes the received data in secondary or specific protocol format into upper-layer messages, and stores them in the message queue output by the protocol decoder. Flush passes the messages in the message queue to the messageReceived method of the subsequent filter. The default protocol encoding output of the protocol codec filter ProtocolCodecFilter is ProtocolEncoderOutputImpl, and the protocol decoding output is SimpleProtocolDecoderOutput.
The structure is as follows:
ProtocolCodecFilter extends IoFilterAdapter
   --ProtocolCodecFactory
      --ProtocolEncoder
         --ProtocolEncoderOutput(ProtocolEncoderOutputImpl)
      --ProtocolDecoder
         --ProtocolDecoderOutput(SimpleProtocolDecoderOutput)

Today we officially enter to analyze the implementation of the protocol codec filter.
//ProtocolCodecFilter
/**
 * An {@link IoFilter} which translates binary or protocol specific data into
 * message object and vice versa using {@link ProtocolCodecFactory},
 * {@link ProtocolEncoder}, or {@link ProtocolDecoder}.
 *
 * @author The Apache Directory Project ([email protected])
 * @version $Rev$, $Date$
 */
public class ProtocolCodecFilter extends IoFilterAdapter
{
    private static final Logger LOGGER = LoggerFactory.getLogger(org/apache/mina/filter/codec/ProtocolCodecFilter);
    private static final Class EMPTY_PARAMS[] = new Class[0];
    private static final IoBuffer EMPTY_BUFFER = IoBuffer.wrap(new byte[0]);
    //Encoder property key
    private static final AttributeKey ENCODER =
         new AttributeKey(org/apache/mina/filter/codec/ProtocolCodecFilter, "encoder");
    //Encoder output attribute key
    private static final AttributeKey ENCODER_OUT =
         new AttributeKey(org/apache/mina/filter/codec/ProtocolCodecFilter, "encoderOut");
    //decoder property key
    private static final AttributeKey DECODER =
         new AttributeKey(org/apache/mina/filter/codec/ProtocolCodecFilter, "decoder");  
    //decoder output attribute key
    private static final AttributeKey DECODER_OUT =
         new AttributeKey(org/apache/mina/filter/codec/ProtocolCodecFilter, "decoderOut");
    private final ProtocolCodecFactory factory;//Protocol codec factory
}

First look at the structure:
//Construct protocol codec filter based on protocol codec
 public ProtocolCodecFilter(ProtocolCodecFactory factory) {
        if (factory == null) {
            throw new NullPointerException("factory");
        }
        this.factory = factory;
    }
//Construct protocol codec filter based on protocol encoder and decoder
    public ProtocolCodecFilter(final ProtocolEncoder encoder,
            final ProtocolDecoder decoder) {
        if (encoder == null) {
            throw new NullPointerException("encoder");
        }
        if (decoder == null) {
            throw new NullPointerException("decoder");
        }
        factory = new ProtocolCodecFactory() {
            public ProtocolEncoder getEncoder() {
                return encoder;
            }
            public ProtocolDecoder getDecoder() {
                return decoder;
            }
        };
    }
//Construct protocol codec filter according to protocol codec class
public ProtocolCodecFilter(final Class encoderClass,
            final Class decoderClass) {
        if (encoderClass == null) {
            throw new NullPointerException("encoderClass");
        }
        if (decoderClass == null) {
            throw new NullPointerException("decoderClass");
        }
	//If the protocol codec type parameter is not ProtocolEncoder, ProtocolDecoder
	//Throw illegal parameter exception
        if (!ProtocolEncoder.class.isAssignableFrom(encoderClass)) {
            throw new IllegalArgumentException("encoderClass: "
                    + encoderClass.getName());
        }
        if (!ProtocolDecoder.class.isAssignableFrom(decoderClass)) {
            throw new IllegalArgumentException("decoderClass: "
                    + decoderClass.getName());
        }
	//Get the protocol codec no-parameter construction
        try {
            encoderClass.getConstructor(EMPTY_PARAMS);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(
                    "encoderClass doesn't have a public default constructor.");
        }
        try {
            decoderClass.getConstructor(EMPTY_PARAMS);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(
                    "decoderClass doesn't have a public default constructor.");
        }
       //Create a protocol codec instance based on the protocol encoder and decoder types, and construct a protocol codec factory
        factory = new ProtocolCodecFactory() {
            public ProtocolEncoder getEncoder() throws Exception {
                return (ProtocolEncoder) encoderClass.newInstance();
            }

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

As can be seen from the above, the protocol codec filter construction, in the final analysis, is to initialize the protocol encoder factory.
Let's look at the related methods of filters
public void onPreAdd(IoFilterChain parent, String name,
            NextFilter nextFilter) throws Exception {
	    //If the filter chain already contains a protocol codec filter, throw an illegal state exception
        if (parent.contains(ProtocolCodecFilter.class)) {
            throw new IllegalStateException(
                    "A filter chain cannot contain more than one ProtocolCodecFilter.");
        }
    }

From the onPreAdd method, two protocol codecs cannot exist on a filter chain, that is, unique.

Let's look at the message sent by the conversation

public void filterWrite(NextFilter nextFilter, IoSession session,
            WriteRequest writeRequest) throws Exception {
	// get message from write request
        Object message = writeRequest.getMessage();
	//If it is a byte buffer, pass it to the subsequent filter
        if (message instanceof ByteBuffer) {
            nextFilter.filterWrite(session, writeRequest);
            return;
        }
        //Get the protocol encoder from the session, and the protocol encoding output
        ProtocolEncoder encoder = getEncoder(session);
        ProtocolEncoderOutputImpl encoderOut = getEncoderOut(session,
                nextFilter, writeRequest);

        try {
	    //encoder encodes the message
            encoder.encode(session, message, encoderOut);
	    //Encoded output flush message queue
            encoderOut.flush();
	    // Pass the message to the next filter
            nextFilter.filterWrite(session, new WriteRequest(
                    new MessageByteBuffer(writeRequest.getMessage()),
                    writeRequest.getFuture(), writeRequest.getDestination()));
        } catch (Throwable t) {
            ProtocolEncoderException pee;
            if (t instanceof ProtocolEncoderException) {
                pee = (ProtocolEncoderException) t;
            } else {
                pee = new ProtocolEncoderException(t);
            }
            throw pee;
        }
    }

Look at these points
1.
//Get the protocol encoder from the session, and the protocol encoding output
  ProtocolEncoder encoder = getEncoder(session);
  ProtocolEncoderOutputImpl encoderOut = getEncoderOut(session,
          nextFilter, writeRequest);

//Get the protocol encoder, this is very simple not to talk about
private ProtocolEncoder getEncoder(IoSession session) throws Exception {
        ProtocolEncoder encoder = (ProtocolEncoder) session
                .getAttribute(ENCODER);
        if (encoder == null) {
            encoder = factory.getEncoder();
            session.setAttribute(ENCODER, encoder);
        }
        return encoder;
    }
    //Get the protocol encoder output
    private ProtocolEncoderOutputImpl getEncoderOut(IoSession session,
            NextFilter nextFilter, WriteRequest writeRequest) {
        return new ProtocolEncoderOutputImpl(session, nextFilter, writeRequest);
    }

2.
// Pass the message to the next filter
nextFilter.filterWrite(session, new WriteRequest(
        new MessageByteBuffer(writeRequest.getMessage()),
        writeRequest.getFuture(), writeRequest.getDestination()));

//Inherit the byte buffer proxy, this has been seen before, not to mention
private static class MessageByteBuffer extends ByteBufferProxy {
    private final Object message;

    private MessageByteBuffer(Object message) {
        super(EMPTY_BUFFER);
        this.message = message;
    }

    public void acquire() {
        // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
    }

    public void release() {
        // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
    }
}

From the session write operation, session#write(filterWrite), first get the message from the write request, if the message is a byte buffer, it is directly passed to the subsequent filter, otherwise, the protocol encoder and protocol encoder output are obtained from the protocol codec factory , the
protocol encoder encodes the encoded message, writes it to the protocol encoder output byte buffer queue,
and then the protocol encoder outputs the flush byte buffer queue.
Looking at messageSent again, you can see it at a glance:
public void messageSent(NextFilter nextFilter, IoSession session,
            Object message) throws Exception {
        if (message instanceof HiddenByteBuffer) {
            return;
        }
        if (!(message instanceof MessageByteBuffer)) {
            nextFilter.messageSent(session, message);
            return;
        }
        nextFilter.messageSent(session, ((MessageByteBuffer) message).message);
    }

Let's look at receiving messages
public void messageReceived(NextFilter nextFilter, IoSession session,
            Object message) throws Exception {
	//If the message is not a byte buffer, it is passed directly to the subsequent filter
        if (!(message instanceof ByteBuffer)) {
            nextFilter.messageReceived(session, message);
            return;
        }
        ByteBuffer in = (ByteBuffer) message;
	//If the byte buffer is empty, return directly
        if (!in.hasRemaining()) {
            in.release();
            return;
        }
        //Get the decoder, and the output of the decoder
        ProtocolDecoder decoder = getDecoder(session);
        ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);
        int oldPos = in.position();
        try {
            synchronized (decoderOut) {
	        //decode byte buffer
                decoder.decode(session, in, decoderOut);
            }
        } catch (Throwable t) {
            ProtocolDecoderException pde;
            if (t instanceof ProtocolDecoderException) {
                pde = (ProtocolDecoderException) t;
            } else {
                pde = new ProtocolDecoderException(t);
            }

            if (pde.getHexdump() == null) {
                int curPos = in.position();
                in.position(oldPos);
                pde.setHexdump(in.getHexDump());
                in.position(curPos);
            }
            throw pde;
        } finally {
            try {
                // Release the read buffer.
		// release the byte buffer
                in.release();
            } finally {
	        //flush decoder output message queue
                decoderOut.flush();
            }
        }
    }

The messageReceived method has 1 point to pay attention to,
//Get the decoder, and the output of the decoder
ProtocolDecoder decoder = getDecoder(session);
ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);

//this is similar to protocol encoder and encoder output
  private ProtocolDecoder getDecoder(IoSession session) throws Exception {
        ProtocolDecoder decoder = (ProtocolDecoder) session
                .getAttribute(DECODER);
        if (decoder == null) {
            decoder = factory.getDecoder();
            session.setAttribute(DECODER, decoder);
        }
        return decoder;
    }

    private ProtocolDecoderOutput getDecoderOut(IoSession session,
            NextFilter nextFilter) {
        ProtocolDecoderOutput out = (ProtocolDecoderOutput) session.getAttribute(DECODER_OUT);
        if (out == null) {
            out = new SimpleProtocolDecoderOutput(session, nextFilter);
            session.setAttribute(DECODER_OUT, out);
        }

        return out;
    }
 

As can be seen from the above, the session receives the message messageReceived. If the message is not a byte buffer, it is directly passed to the
subsequent filter, otherwise, the protocol decoder is obtained, and the protocol decoder output, the protocol decoder decodes the byte buffer to upload the message object, Write to the protocol decoder output message queue, and finally the decoder output flush message queue.
Look at the session close again
public void sessionClosed(NextFilter nextFilter, IoSession session)
            throws Exception {
        // Call finishDecode() first when a connection is closed.
	//Get the decoder and decoder output
        ProtocolDecoder decoder = getDecoder(session);
        ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);
        try {
	    //decoder, decoding session did not decode data
            decoder.finishDecode(session, decoderOut);
        } catch (Throwable t) {
            ProtocolDecoderException pde;
            if (t instanceof ProtocolDecoderException) {
                pde = (ProtocolDecoderException) t;
            } else {
                pde = new ProtocolDecoderException(t);
            }
            throw pde;
        } finally {
            // Dispose all.
	    //Release encoder, decoder and decoder output related resources, flush decoder output message queue
            disposeEncoder(session);
            disposeDecoder(session);
            disposeDecoderOut(session);
            decoderOut.flush();
        }
        nextFilter.sessionClosed(session);
}

Look at the final resource release of the method:
// release the decoder resources
private void disposeEncoder(IoSession session) {
        //Remove the encoder property
        ProtocolEncoder encoder = (ProtocolEncoder) session
                .removeAttribute(ENCODER);
        if (encoder == null) {
            return;
        }

        try {
	   //Release session encoder related resources
            encoder.dispose(session);
        } catch (Throwable t) {
            SessionLog.warn(session, "Failed to dispose: "
                    + encoder.getClass().getName() + " (" + encoder + ')');
        }
    }
   //Remove the decoder property from the session, release the session decoder resource
    private void disposeDecoder(IoSession session) {
        ProtocolDecoder decoder = (ProtocolDecoder) session
                .removeAttribute(DECODER);
        if (decoder == null) {
            return;
        }

        try {
            decoder.dispose(session);
        } catch (Throwable t) {
            SessionLog.warn(session, "Falied to dispose: "
                    + decoder.getClass().getName() + " (" + decoder + ')');
        }
    }
//Remove decode output attribute from session
 private void disposeDecoderOut(IoSession session) {
        session.removeAttribute(DECODER_OUT);
    }

From the above, the session is closed, mainly because the decoder decodes the undecoded data of the session, writes it to the decoder output message queue, and
finally releases the encoder, decoder and decoder output related resources, and flushes the decoder output message queue.

Let's look at removing the protocol codec filter onPostRemove
public void onPostRemove(IoFilterChain parent, String name,
            NextFilter nextFilter) throws Exception {
        disposeEncoder(parent.getSession());//Remove the encoder attribute from the session and release the session encoder related resources
        disposeDecoder(parent.getSession());//Remove the decoder property from the session and release the session decoder resource
        disposeDecoderOut(parent.getSession());//Remove the decode output attribute from the session
    }

As can be seen from the above, after removing the protocol codec filter from the filter chain, it is necessary to release the session codec,
decode output attributes, and release related resources.

Summary:
The protocol codec filter construction is mainly to initialize the protocol encoder factory. Two protocol codecs cannot exist on a filter chain, i.e. unique. For session write operation, see session#write(filterWrite), first get the message from the write request, if the message is a byte buffer, it is directly passed to the subsequent filter, otherwise, the protocol encoder and protocol encoder output are obtained from the protocol codec factory, The protocol encoder encodes the encoded message, writes it to the protocol encoder output byte buffer queue,
and then the protocol encoder output flushes the byte buffer queue. The session receives the message messageReceived. If the message is not a byte buffer, it is directly passed to the subsequent filter. Otherwise, the protocol decoder is obtained and the output of the protocol decoder is obtained. The byte buffer decoded by the protocol decoder is the upload message object, which is written to the output of the protocol decoder Message queue, and finally the decoder outputs flush message queue. The session is closed, mainly because the decoder decodes the undecoded data of the session, writes it to the decoder output message queue, and finally removes the encoder, decoder and decoder output attributes from the session, and releases the encoder, decoder and decoder output related resources. flush the decoder output message queue. After the protocol codec filter is removed from the filter chain, it is necessary to release the session codec, decode output attributes, and release related resources.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326426592&siteId=291194637