You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jv...@apache.org on 2013/03/26 13:26:45 UTC

git commit: new try for a codec API

Updated Branches:
  refs/heads/trunk cb7896a19 -> 1a4ad866e


new try for a codec API


Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/1a4ad866
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/1a4ad866
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/1a4ad866

Branch: refs/heads/trunk
Commit: 1a4ad866eb25e509a8eb4b5d52e4660152cc5ded
Parents: cb7896a
Author: jvermillard <jv...@apache.org>
Authored: Tue Mar 26 13:26:04 2013 +0100
Committer: jvermillard <jv...@apache.org>
Committed: Tue Mar 26 13:26:04 2013 +0100

----------------------------------------------------------------------
 .../org/apache/mina/codec/ProtocolDecoder.java     |   23 ++-
 .../org/apache/mina/codec/ProtocolEncoder.java     |   20 ++-
 .../mina/codec/StatelessProtocolDecoder.java       |   34 +++
 .../mina/codec/StatelessProtocolEncoder.java       |   35 +++
 .../mina/filter/codec/ProtocolCodecFactory.java    |   49 ----
 .../mina/filter/codec/ProtocolCodecFilter.java     |  170 +++------------
 .../mina/filter/codec/ProtocolCodecFilterTest.java |   82 -------
 .../org/apache/mina/examples/http/HttpTest.java    |    6 +-
 .../org/apache/mina/examples/http/HttpsTest.java   |    6 +-
 .../org/apache/mina/http/HttpDecoderState.java     |   64 ++++++
 .../org/apache/mina/http/HttpServerDecoder.java    |   48 +++--
 .../org/apache/mina/http/HttpServerEncoder.java    |   15 +-
 .../apache/mina/http/HttpServerDecoderTest.java    |   30 ++-
 13 files changed, 269 insertions(+), 313 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/codec/src/main/java/org/apache/mina/codec/ProtocolDecoder.java
----------------------------------------------------------------------
diff --git a/codec/src/main/java/org/apache/mina/codec/ProtocolDecoder.java b/codec/src/main/java/org/apache/mina/codec/ProtocolDecoder.java
index 8c4ffdd..c1dea93 100644
--- a/codec/src/main/java/org/apache/mina/codec/ProtocolDecoder.java
+++ b/codec/src/main/java/org/apache/mina/codec/ProtocolDecoder.java
@@ -19,29 +19,44 @@
  */
 package org.apache.mina.codec;
 
+import java.nio.ByteBuffer;
+
 /**
  * Decodes binary or protocol-specific data into higher-level message objects.
  * 
- * Should be state-full, and have one instance per new session.
+ * Must be immutable, a CONTEXT will be provided per session.
+ * 
+ * @param <INPUT> the incoming message to decode (the low level message, usually a {@link ByteBuffer})
+ * @param <OUTPUT> the decoded message (your high level protocol Pojo/DTO)
+ * @param <DECODING_STATE> the context where to save the current decoding state (e.g. accumulated bytes, encoding
+ *        context switching..)
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  * 
  */
-public interface ProtocolDecoder<INPUT, OUTPUT> {
+public interface ProtocolDecoder<INPUT, OUTPUT, DECODING_STATE> {
+
+    /**
+     * Create a new session context for this decoder
+     * 
+     * @return the newly created context
+     */
+    DECODING_STATE createDecoderState();
 
     /**
      * Decode binary or protocol-specific content of type <code>INPUT</code> into higher-level protocol message objects,
      * of type OUTPUT
      * 
      * @param input the received message to decode
+     * @param context the decoding context (will be stored in the session for the next decode call)
      * @return the decoded messages or <code>null</code> if nothing was decoded
      * @throws ProtocolDecoderException if something wrong happen during decoding (e.g. : a malformed input message)
      */
-    OUTPUT[] decode(INPUT input) throws ProtocolDecoderException;
+    OUTPUT[] decode(INPUT input, DECODING_STATE context) throws ProtocolDecoderException;
 
     /**
      * Finish decoding, for example if the decoder accumulated some unused input, it should discard it, or throw an
      * Exception
      */
-    void finishDecode();
+    void finishDecode(DECODING_STATE context);
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/codec/src/main/java/org/apache/mina/codec/ProtocolEncoder.java
----------------------------------------------------------------------
diff --git a/codec/src/main/java/org/apache/mina/codec/ProtocolEncoder.java b/codec/src/main/java/org/apache/mina/codec/ProtocolEncoder.java
index f439f51..de48ed9 100644
--- a/codec/src/main/java/org/apache/mina/codec/ProtocolEncoder.java
+++ b/codec/src/main/java/org/apache/mina/codec/ProtocolEncoder.java
@@ -25,15 +25,31 @@ import java.nio.ByteBuffer;
  * In charge of encoding a message of type MESSAGE into another form (could be a {@link ByteBuffer} or any other
  * protocol level construction.
  * 
+ * Must be immutable, a CONTEXT will be provided per session.
+ * 
+ * @param <INPUT> the incoming message to encode (your high level protocol Pojo/DTO)
+ * @param <OUTPUT> the encoded message (the low level message, usually a {@link ByteBuffer})
+ * @param <ENCODING_STATE> the context where to save the current encoding state (e.g. encoding context switching..)
+ * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  * 
  */
-public interface ProtocolEncoder<INPUT /* message type */, OUTPUT> {
+public interface ProtocolEncoder<INPUT /* message type */, OUTPUT, ENCODING_STATE> {
+
+    /**
+     * Create a new session context for this decoder
+     * 
+     * @return the newly created context
+     */
+    ENCODING_STATE createEncoderState();
 
     /**
      * Encodes higher-level message objects of type <code>INPUT</code> into binary or protocol-specific data of type
      * <code>OUTPUT</code>.
+     * 
+     * @param message the message to encode
+     * @param context the encoding context (will be stored in the session for the next decode call)
      */
-    OUTPUT encode(INPUT message);
+    OUTPUT encode(INPUT message, ENCODING_STATE context);
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/codec/src/main/java/org/apache/mina/codec/StatelessProtocolDecoder.java
----------------------------------------------------------------------
diff --git a/codec/src/main/java/org/apache/mina/codec/StatelessProtocolDecoder.java b/codec/src/main/java/org/apache/mina/codec/StatelessProtocolDecoder.java
new file mode 100644
index 0000000..e33c2f2
--- /dev/null
+++ b/codec/src/main/java/org/apache/mina/codec/StatelessProtocolDecoder.java
@@ -0,0 +1,34 @@
+/*
+ *  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.codec;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A state less {@link ProtocolDecoder} : no context stored for the different sessions.
+ * 
+ * @param <INPUT> the incoming message to decode (the low level message, usually a {@link ByteBuffer})
+ * @param <OUTPUT> the decoded message (your high level protocol Pojo/DTO)
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public interface StatelessProtocolDecoder<INPUT, OUTPUT> extends ProtocolDecoder<INPUT, OUTPUT, Void> {
+
+}

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/codec/src/main/java/org/apache/mina/codec/StatelessProtocolEncoder.java
----------------------------------------------------------------------
diff --git a/codec/src/main/java/org/apache/mina/codec/StatelessProtocolEncoder.java b/codec/src/main/java/org/apache/mina/codec/StatelessProtocolEncoder.java
new file mode 100644
index 0000000..840676e
--- /dev/null
+++ b/codec/src/main/java/org/apache/mina/codec/StatelessProtocolEncoder.java
@@ -0,0 +1,35 @@
+/*
+ *  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.codec;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A state less {@link ProtocolEncoder} : no context stored for the different sessions.
+ * 
+ * @param <INPUT> the incoming message to encode (your high level protocol Pojo/DTO)
+ * @param <OUTPUT> the encoded message (the low level message, usually a {@link ByteBuffer})
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public interface StatelessProtocolEncoder<INPUT, OUTPUT> extends ProtocolEncoder<INPUT, OUTPUT, Void> {
+
+}

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFactory.java b/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFactory.java
deleted file mode 100644
index 1f72812..0000000
--- a/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  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;
-
-import org.apache.mina.api.IoSession;
-import org.apache.mina.codec.ProtocolDecoder;
-import org.apache.mina.codec.ProtocolEncoder;
-
-/**
- * Provides {@link ProtocolEncoder} and {@link ProtocolDecoder} which translates
- * binary or protocol specific data into message object and vice versa.
- * <p>
- * Please refer to
- * <a href="../../../../../xref-examples/org/apache/mina/examples/reverser/ReverseProtocolProvider.html"><code>ReverserProtocolProvider</code></a>
- * example.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public interface ProtocolCodecFactory<MESSAGE,ENCODED> {
-
-    /**
-     * Returns a new (or reusable) instance of {@link ProtocolEncoder} which
-     * encodes message objects into binary or protocol-specific data.
-     */
-    ProtocolEncoder<MESSAGE,ENCODED> getEncoder(IoSession session);
-
-    /**
-     * Returns a new (or reusable) instance of {@link ProtocolDecoder} which
-     * decodes binary or protocol-specific data into message objects.
-     */
-    ProtocolDecoder<ENCODED,MESSAGE> getDecoder(IoSession session);
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFilter.java b/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFilter.java
index bd8ef2e..e538acb 100644
--- a/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFilter.java
+++ b/core/src/main/java/org/apache/mina/filter/codec/ProtocolCodecFilter.java
@@ -39,112 +39,32 @@ import org.slf4j.LoggerFactory;
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
-public class ProtocolCodecFilter<MESSAGE, ENCODED> extends AbstractIoFilter {
+public class ProtocolCodecFilter<MESSAGE, ENCODED, ENCODING_STATE, DECODING_STATE> extends AbstractIoFilter {
     /** A logger for this class */
     private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolCodecFilter.class);
 
-    private static final Class<?>[] EMPTY_PARAMS = new Class[0];
+    /** the immutable encoder */
+    private final ProtocolEncoder<MESSAGE, ENCODED, ENCODING_STATE> encoder;
+
+    /** the immutable decoder */
+    private final ProtocolDecoder<ENCODED, MESSAGE, DECODING_STATE> decoder;
 
     /** key for session attribute holding the encoder */
-    @SuppressWarnings("rawtypes")
-    private final AttributeKey<ProtocolEncoder> ENCODER = new AttributeKey<ProtocolEncoder>(ProtocolEncoder.class,
-            "internal_encoder");
+    private final AttributeKey<Object> ENCODER = new AttributeKey<Object>(Object.class, "internal_encoder");
 
     /** key for session attribute holding the decoder */
-    @SuppressWarnings("rawtypes")
-    private final AttributeKey<ProtocolDecoder> DECODER = new AttributeKey<ProtocolDecoder>(ProtocolDecoder.class,
-            "internal_decoder");
-
-    /** The factory responsible for creating the encoder and decoder */
-    private final ProtocolCodecFactory<MESSAGE, ENCODED> factory;
-
-    /**
-     * 
-     * Creates a new instance of ProtocolCodecFilter, associating a factory for the creation of the encoder and decoder.
-     * 
-     * @param factory The associated factory
-     */
-    public ProtocolCodecFilter(ProtocolCodecFactory<MESSAGE, ENCODED> factory) {
-        if (factory == null) {
-            throw new IllegalArgumentException("factory");
-        }
-
-        this.factory = factory;
-    }
-
-    /**
-     * Creates a new instance of ProtocolCodecFilter, without any factory. The encoder/decoder factory will be created
-     * as an anonymous class, using the two parameters (encoder and decoder), which are class names. Instances for those
-     * classes will be created in this constructor.
-     * 
-     * @param encoderClass The class responsible for encoding the message
-     * @param decoderClass The class responsible for decoding the message
-     */
-    public ProtocolCodecFilter(Class<? extends ProtocolEncoder<MESSAGE, ENCODED>> encoderClass,
-            Class<? extends ProtocolDecoder<ENCODED, MESSAGE>> decoderClass) {
-        Assert.assertNotNull(encoderClass, "Encoder Class");
-        Assert.assertNotNull(decoderClass, "Decoder Class");
-
-        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.");
-        }
-
-        final ProtocolEncoder<MESSAGE, ENCODED> encoder;
-
-        try {
-            encoder = encoderClass.newInstance();
-        } catch (Exception e) {
-            throw new IllegalArgumentException("encoderClass cannot be initialized");
-        }
-
-        final ProtocolDecoder<ENCODED, MESSAGE> decoder;
-
-        try {
-            decoder = decoderClass.newInstance();
-        } catch (Exception e) {
-            throw new IllegalArgumentException("decoderClass cannot be initialized");
-        }
-
-        // Create the inner factory based on the two parameters.
-        this.factory = new ProtocolCodecFactory<MESSAGE, ENCODED>() {
-            @Override
-            public ProtocolEncoder<MESSAGE, ENCODED> getEncoder(IoSession session) {
-                return encoder;
-            }
-
-            @Override
-            public ProtocolDecoder<ENCODED, MESSAGE> getDecoder(IoSession session) {
-                return decoder;
-            }
-        };
-    }
-
-    /**
-     * Get the encoder instance from a given session.
-     * 
-     * @param session The associated session we will get the encoder from
-     * @return The encoder instance, if any
-     */
-    public ProtocolEncoder<MESSAGE, ENCODED> getEncoder(IoSession session) {
-        return factory.getEncoder(session);
-    }
+    private final AttributeKey<Object> DECODER = new AttributeKey<Object>(Object.class, "internal_decoder");
 
     /**
-     * Get the decoder instance from a given session.
+     * Creates a new instance of ProtocolCodecFilter, with the specified encoder and decoder.
      * 
-     * @param session The associated session we will get the decoder from
-     * @return The decoder instance, if any
      */
-    public ProtocolDecoder<ENCODED, MESSAGE> getDecoder(IoSession session) {
-        return factory.getDecoder(session);
+    public ProtocolCodecFilter(ProtocolEncoder<MESSAGE, ENCODED, ENCODING_STATE> encoder,
+            ProtocolDecoder<ENCODED, MESSAGE, DECODING_STATE> decoder) {
+        Assert.assertNotNull(encoder, "encoder");
+        Assert.assertNotNull(decoder, "decoder");
+        this.encoder = encoder;
+        this.decoder = decoder;
     }
 
     /**
@@ -162,12 +82,12 @@ public class ProtocolCodecFilter<MESSAGE, ENCODED> extends AbstractIoFilter {
     public void messageReceived(IoSession session, Object in, ReadFilterChainController controller) {
         LOGGER.debug("Processing a MESSAGE_RECEIVED for session {}", session);
 
-        ProtocolDecoder<ENCODED, MESSAGE> decoder = getDecoder(session);
+        DECODING_STATE state = getDecodingState(session);
 
-        // Loop until the codec cannot decode more
+        // Loop until the decoder cannot decode more
         MESSAGE[] msg;
         try {
-            while ((msg = decoder.decode((ENCODED) in)) != null) {
+            while ((msg = decoder.decode((ENCODED) in, state)) != null) {
                 for (MESSAGE m : msg) {
                     controller.callReadNextFilter(m);
                 }
@@ -185,8 +105,7 @@ public class ProtocolCodecFilter<MESSAGE, ENCODED> extends AbstractIoFilter {
     public void messageWriting(IoSession session, WriteRequest message, WriteFilterChainController controller) {
         LOGGER.debug("Processing a MESSAGE_WRITTING for session {}", session);
 
-        ProtocolEncoder<MESSAGE, ENCODED> encoder = getEncoder(session);
-        ENCODED encoded = encoder.encode((MESSAGE) message.getMessage());
+        ENCODED encoded = encoder.encode((MESSAGE) message.getMessage(), getEncodingState(session));
         message.setMessage(encoded);
 
         controller.callWriteNextFilter(message);
@@ -197,13 +116,13 @@ public class ProtocolCodecFilter<MESSAGE, ENCODED> extends AbstractIoFilter {
      */
     @Override
     public void sessionOpened(IoSession session) {
-        // Initialize the encoder and decoder if we use a factory
-        if (factory != null) {
-            ProtocolEncoder<MESSAGE, ENCODED> encoder = factory.getEncoder(session);
-            session.setAttribute(ENCODER, encoder);
-            ProtocolDecoder<ENCODED, MESSAGE> decoder = factory.getDecoder(session);
-            session.setAttribute(DECODER, decoder);
-        }
+        // Initialize the encoder and decoder state
+
+        ENCODING_STATE encodingState = encoder.createEncoderState();
+        session.setAttribute(ENCODER, encodingState);
+
+        DECODING_STATE decodingState = decoder.createDecoderState();
+        session.setAttribute(DECODER, decodingState);
     }
 
     /**
@@ -211,39 +130,20 @@ public class ProtocolCodecFilter<MESSAGE, ENCODED> extends AbstractIoFilter {
      */
     @Override
     public void sessionClosed(IoSession session) {
-        disposeCodec(session);
+        decoder.finishDecode(getDecodingState(session));
+
     }
 
     // ----------- Helper methods ---------------------------------------------
-    /**
-     * Dispose the encoder, decoder, and the callback for the decoded messages.
-     */
-    private void disposeCodec(IoSession session) {
-        // We just remove the two instances of encoder/decoder to release resources
-        // from the session
-        disposeEncoder(session);
-        disposeDecoder(session);
-    }
 
-    /**
-     * Dispose the encoder, removing its instance from the session's attributes, and calling the associated dispose
-     * method.
-     */
-    private void disposeEncoder(IoSession session) {
-        session.removeAttribute(ENCODER);
+    @SuppressWarnings("unchecked")
+    private DECODING_STATE getDecodingState(IoSession session) {
+        return (DECODING_STATE) session.getAttribute(DECODER);
     }
 
-    /**
-     * Dispose the decoder, removing its instance from the session's attributes, and calling the associated dispose
-     * method.
-     */
-    private void disposeDecoder(IoSession session) {
-        @SuppressWarnings("unchecked")
-        ProtocolDecoder<ENCODED, MESSAGE> decoder = session.removeAttribute(DECODER);
-        try {
-            decoder.finishDecode();
-        } catch (Throwable t) {
-            LOGGER.warn("Failed to dispose: " + decoder.getClass().getName() + " (" + decoder + ')');
-        }
+    @SuppressWarnings("unchecked")
+    private ENCODING_STATE getEncodingState(IoSession session) {
+        return (ENCODING_STATE) session.getAttribute(ENCODER);
     }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/core/src/test/java/org/apache/mina/filter/codec/ProtocolCodecFilterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/mina/filter/codec/ProtocolCodecFilterTest.java b/core/src/test/java/org/apache/mina/filter/codec/ProtocolCodecFilterTest.java
deleted file mode 100644
index f70415f..0000000
--- a/core/src/test/java/org/apache/mina/filter/codec/ProtocolCodecFilterTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * 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;
-
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-import org.apache.mina.api.IoSession;
-import org.apache.mina.codec.ProtocolDecoder;
-import org.apache.mina.codec.ProtocolEncoder;
-import org.junit.Test;
-
-/**
- * Unit test for {@link ProtocolCodecFilter}
- * 
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class ProtocolCodecFilterTest {
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Test
-    public void constructor_args() {
-        try {
-            new ProtocolCodecFilter(null);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // happy
-        } catch (Exception e2) {
-            fail();
-        }
-
-        try {
-            new ProtocolCodecFilter(null, ProtocolDecoder.class);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // happy
-        } catch (Exception e2) {
-            fail();
-        }
-
-        try {
-            new ProtocolCodecFilter(ProtocolEncoder.class, null);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // happy
-        } catch (Exception e2) {
-            fail();
-        }
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Test
-    public void codec_factory() {
-        ProtocolEncoder encoder = mock(ProtocolEncoder.class);
-        ProtocolDecoder decoder = mock(ProtocolDecoder.class);
-
-        ProtocolCodecFactory factory = mock(ProtocolCodecFactory.class);
-        when(factory.getEncoder(any(IoSession.class))).thenReturn(encoder);
-        when(factory.getDecoder(any(IoSession.class))).thenReturn(decoder);
-
-        ProtocolCodecFilter codec = new ProtocolCodecFilter(factory);
-        assertEquals(encoder, codec.getEncoder(null));
-        assertEquals(decoder, codec.getDecoder(null));
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/examples/src/main/java/org/apache/mina/examples/http/HttpTest.java
----------------------------------------------------------------------
diff --git a/examples/src/main/java/org/apache/mina/examples/http/HttpTest.java b/examples/src/main/java/org/apache/mina/examples/http/HttpTest.java
index f62a712..1a34838 100644
--- a/examples/src/main/java/org/apache/mina/examples/http/HttpTest.java
+++ b/examples/src/main/java/org/apache/mina/examples/http/HttpTest.java
@@ -32,6 +32,7 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter;
 import org.apache.mina.filter.logging.LoggingFilter;
 import org.apache.mina.filterchain.ReadFilterChainController;
 import org.apache.mina.http.DateUtil;
+import org.apache.mina.http.HttpDecoderState;
 import org.apache.mina.http.HttpServerDecoder;
 import org.apache.mina.http.HttpServerEncoder;
 import org.apache.mina.http.api.DefaultHttpResponse;
@@ -50,8 +51,9 @@ public class HttpTest {
 
         NioTcpServer httpServer = new NioTcpServer();
         httpServer.setReuseAddress(true);
-        httpServer.setFilters(new LoggingFilter("INCOMING"), new ProtocolCodecFilter<HttpPdu, ByteBuffer>(
-                HttpServerEncoder.class, HttpServerDecoder.class), new LoggingFilter("DECODED"), new DummyHttpSever());
+        httpServer.setFilters(new LoggingFilter("INCOMING"),
+                new ProtocolCodecFilter<HttpPdu, ByteBuffer, Void, HttpDecoderState>(new HttpServerEncoder(),
+                        new HttpServerDecoder()), new LoggingFilter("DECODED"), new DummyHttpSever());
 
         httpServer.getSessionConfig().setTcpNoDelay(true);
 

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/examples/src/main/java/org/apache/mina/examples/http/HttpsTest.java
----------------------------------------------------------------------
diff --git a/examples/src/main/java/org/apache/mina/examples/http/HttpsTest.java b/examples/src/main/java/org/apache/mina/examples/http/HttpsTest.java
index 12fedf4..a969163 100644
--- a/examples/src/main/java/org/apache/mina/examples/http/HttpsTest.java
+++ b/examples/src/main/java/org/apache/mina/examples/http/HttpsTest.java
@@ -32,6 +32,7 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter;
 import org.apache.mina.filter.logging.LoggingFilter;
 import org.apache.mina.filterchain.ReadFilterChainController;
 import org.apache.mina.http.DateUtil;
+import org.apache.mina.http.HttpDecoderState;
 import org.apache.mina.http.HttpServerDecoder;
 import org.apache.mina.http.HttpServerEncoder;
 import org.apache.mina.http.api.DefaultHttpResponse;
@@ -49,8 +50,9 @@ public class HttpsTest {
 
         NioTcpServer httpServer = new NioTcpServer();
 
-        httpServer.setFilters(new LoggingFilter("INCOMING"), new ProtocolCodecFilter<HttpPdu, ByteBuffer>(
-                HttpServerEncoder.class, HttpServerDecoder.class), new LoggingFilter("DECODED"), new DummyHttpSever());
+        httpServer.setFilters(new LoggingFilter("INCOMING"),
+                new ProtocolCodecFilter<HttpPdu, ByteBuffer, Void, HttpDecoderState>(new HttpServerEncoder(),
+                        new HttpServerDecoder()), new LoggingFilter("DECODED"), new DummyHttpSever());
 
         httpServer.getSessionConfig().setTcpNoDelay(true);
 

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/http/src/main/java/org/apache/mina/http/HttpDecoderState.java
----------------------------------------------------------------------
diff --git a/http/src/main/java/org/apache/mina/http/HttpDecoderState.java b/http/src/main/java/org/apache/mina/http/HttpDecoderState.java
new file mode 100644
index 0000000..73c5986
--- /dev/null
+++ b/http/src/main/java/org/apache/mina/http/HttpDecoderState.java
@@ -0,0 +1,64 @@
+/*
+ *  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.http;
+
+import java.nio.ByteBuffer;
+
+/**
+ * State of a HTTP decoding for a given session.
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class HttpDecoderState {
+
+    /** State of the decoder */
+    private DecoderState state = DecoderState.NEW;
+
+    /** The previously received buffer, not totally decoded */
+    private ByteBuffer partial;
+
+    /** Number of bytes remaining to read for completing the body */
+    private int remainingBytes;
+
+    public DecoderState getState() {
+        return state;
+    }
+
+    public void setState(DecoderState state) {
+        this.state = state;
+    }
+
+    public ByteBuffer getPartial() {
+        return partial;
+    }
+
+    public void setPartial(ByteBuffer partial) {
+        this.partial = partial;
+    }
+
+    public int getRemainingBytes() {
+        return remainingBytes;
+    }
+
+    public void setRemainingBytes(int remainingBytes) {
+        this.remainingBytes = remainingBytes;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
----------------------------------------------------------------------
diff --git a/http/src/main/java/org/apache/mina/http/HttpServerDecoder.java b/http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
index 94e084f..f99caef 100644
--- a/http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
+++ b/http/src/main/java/org/apache/mina/http/HttpServerDecoder.java
@@ -40,7 +40,7 @@ import org.slf4j.LoggerFactory;
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
-public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu> {
+public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu, HttpDecoderState> {
     private static final Logger LOG = LoggerFactory.getLogger(HttpServerDecoder.class);
 
     /** Regex to parse HttpRequest Request Line */
@@ -67,26 +67,29 @@ public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu> {
     /** Regex to split cookie header following RFC6265 Section 5.4 */
     public static final Pattern COOKIE_SEPARATOR_PATTERN = Pattern.compile(";");
 
-    /** State of the decoder */
-    private DecoderState state = DecoderState.NEW;
-
-    /** The previously received buffer, not totally decoded */
-    private ByteBuffer partial;
-
-    /** Number of bytes remaining to read for completing the body */
-    private int remainingBytes;
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public HttpDecoderState createDecoderState() {
+        return new HttpDecoderState();
+    }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public HttpPdu[] decode(ByteBuffer msg) throws ProtocolDecoderException {
+    public HttpPdu[] decode(ByteBuffer msg, HttpDecoderState context) throws ProtocolDecoderException {
         LOG.debug("decode : {}", msg);
         if (msg.remaining() <= 0) {
             return null;
         }
-        switch (state) {
+        switch (context.getState()) {
         case HEAD:
             LOG.debug("decoding HEAD");
             // concat the old buffer and the new incoming one
-            msg = ByteBuffer.allocate(partial.remaining() + msg.remaining()).put(partial).put(msg);
+            msg = ByteBuffer.allocate(context.getPartial().remaining() + msg.remaining()).put(context.getPartial())
+                    .put(msg);
             msg.flip();
             // now let's decode like it was a new message
 
@@ -96,9 +99,9 @@ public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu> {
 
             if (rq == null) {
                 // we copy the incoming BB because it's going to be recycled by the inner IoProcessor for next reads
-                partial = ByteBuffer.allocate(msg.remaining());
-                partial.put(msg);
-                partial.flip();
+                context.setPartial(ByteBuffer.allocate(msg.remaining()));
+                context.getPartial().put(msg);
+                context.getPartial().flip();
             } else {
                 return new HttpPdu[] { rq };
             }
@@ -109,18 +112,18 @@ public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu> {
             // send the chunk of body
             HttpContentChunk chunk = new HttpContentChunk(msg);
             // do we have reach end of body ?
-            remainingBytes -= chunkSize;
+            context.setRemainingBytes(context.getRemainingBytes() - chunkSize);
 
-            if (remainingBytes <= 0) {
+            if (context.getRemainingBytes() <= 0) {
                 LOG.debug("end of HTTP body");
-                state = DecoderState.NEW;
-                remainingBytes = 0;
+                context.setState(DecoderState.NEW);
+                context.setRemainingBytes(0);
                 return new HttpPdu[] { chunk, new HttpEndOfContent() };
             }
             break;
 
         default:
-            throw new RuntimeException("Unknonwn decoder state : " + state);
+            throw new RuntimeException("Unknonwn decoder state : " + context.getState());
         }
 
         return null;
@@ -158,8 +161,11 @@ public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu> {
         return new HttpRequestImpl(version, method, requestedPath, generalHeaders);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public void finishDecode() {
+    public void finishDecode(HttpDecoderState context) {
 
     }
 }

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/http/src/main/java/org/apache/mina/http/HttpServerEncoder.java
----------------------------------------------------------------------
diff --git a/http/src/main/java/org/apache/mina/http/HttpServerEncoder.java b/http/src/main/java/org/apache/mina/http/HttpServerEncoder.java
index 8101228..1dd58d4 100644
--- a/http/src/main/java/org/apache/mina/http/HttpServerEncoder.java
+++ b/http/src/main/java/org/apache/mina/http/HttpServerEncoder.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.Map;
 
-import org.apache.mina.codec.ProtocolEncoder;
+import org.apache.mina.codec.StatelessProtocolEncoder;
 import org.apache.mina.http.api.HttpContentChunk;
 import org.apache.mina.http.api.HttpEndOfContent;
 import org.apache.mina.http.api.HttpPdu;
@@ -36,13 +36,22 @@ import org.apache.mina.http.api.HttpResponse;
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
-public class HttpServerEncoder implements ProtocolEncoder<HttpPdu, ByteBuffer> {
+public class HttpServerEncoder implements StatelessProtocolEncoder<HttpPdu, ByteBuffer> {
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public ByteBuffer encode(HttpPdu message) {
+    public Void createEncoderState() {
+        // steless, so it's not used
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ByteBuffer encode(HttpPdu message, Void context) {
         return message.encode(visitor);
     }
 

http://git-wip-us.apache.org/repos/asf/mina/blob/1a4ad866/http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
----------------------------------------------------------------------
diff --git a/http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java b/http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
index 8e18381..f811759 100644
--- a/http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
+++ b/http/src/test/java/org/apache/mina/http/HttpServerDecoderTest.java
@@ -19,8 +19,7 @@
  */
 package org.apache.mina.http;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
@@ -35,44 +34,49 @@ import org.junit.Test;
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public class HttpServerDecoderTest {
-    
+
     @Test
-    public void verifyThatHeaderWithoutLeadingSpaceIsSupported() throws UnsupportedEncodingException, ProtocolDecoderException {
+    public void verifyThatHeaderWithoutLeadingSpaceIsSupported() throws UnsupportedEncodingException,
+            ProtocolDecoderException {
         String reqStr = "GET / HTTP/1.0\r\nHost:localhost\r\n\r\n";
         ByteBuffer buffer = ByteBuffer.allocate(reqStr.length());
         buffer.put(reqStr.getBytes("US-ASCII"));
         buffer.rewind();
         HttpServerDecoder decoder = new HttpServerDecoder();
-        HttpPdu[] pdus = decoder.decode(buffer);
+        HttpDecoderState state = decoder.createDecoderState();
+        HttpPdu[] pdus = decoder.decode(buffer, state);
         assertNotNull(pdus);
         assertEquals(1, pdus.length);
-        assertEquals("localhost", ((HttpRequestImpl)pdus[0]).getHeader("host"));
+        assertEquals("localhost", ((HttpRequestImpl) pdus[0]).getHeader("host"));
     }
 
     @Test
-    public void verifyThatLeadingSpacesAreRemovedFromHeader() throws UnsupportedEncodingException, ProtocolDecoderException {
+    public void verifyThatLeadingSpacesAreRemovedFromHeader() throws UnsupportedEncodingException,
+            ProtocolDecoderException {
         String reqStr = "GET / HTTP/1.0\r\nHost:  localhost\r\n\r\n";
         ByteBuffer buffer = ByteBuffer.allocate(reqStr.length());
         buffer.put(reqStr.getBytes("US-ASCII"));
         buffer.rewind();
         HttpServerDecoder decoder = new HttpServerDecoder();
-        HttpPdu[] pdus = decoder.decode(buffer);
+        HttpDecoderState state = decoder.createDecoderState();
+        HttpPdu[] pdus = decoder.decode(buffer, state);
         assertNotNull(pdus);
         assertEquals(1, pdus.length);
-        assertEquals("localhost", ((HttpRequestImpl)pdus[0]).getHeader("host"));
+        assertEquals("localhost", ((HttpRequestImpl) pdus[0]).getHeader("host"));
     }
 
-
     @Test
-    public void verifyThatTrailingSpacesAreRemovedFromHeader() throws UnsupportedEncodingException, ProtocolDecoderException {
+    public void verifyThatTrailingSpacesAreRemovedFromHeader() throws UnsupportedEncodingException,
+            ProtocolDecoderException {
         String reqStr = "GET / HTTP/1.0\r\nHost:localhost  \r\n\r\n";
         ByteBuffer buffer = ByteBuffer.allocate(reqStr.length());
         buffer.put(reqStr.getBytes("US-ASCII"));
         buffer.rewind();
         HttpServerDecoder decoder = new HttpServerDecoder();
-        HttpPdu[] pdus = decoder.decode(buffer);
+        HttpDecoderState state = decoder.createDecoderState();
+        HttpPdu[] pdus = decoder.decode(buffer, state);
         assertNotNull(pdus);
         assertEquals(1, pdus.length);
-        assertEquals("localhost", ((HttpRequestImpl)pdus[0]).getHeader("host"));
+        assertEquals("localhost", ((HttpRequestImpl) pdus[0]).getHeader("host"));
     }
 }