PROTON-1876 Expose the encode / decode methods using buffers

Expose the encode and decode methods that use WritableBuffer and
ReadableBuffer on the Message interface to allow clients to use newer
no-copy variants of the API easier.
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/message/Message.java b/proton-j/src/main/java/org/apache/qpid/proton/message/Message.java
index 41945fa..db290e3 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/message/Message.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/message/Message.java
@@ -27,7 +27,8 @@
 import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
 import org.apache.qpid.proton.amqp.messaging.Properties;
 import org.apache.qpid.proton.amqp.messaging.Section;
-
+import org.apache.qpid.proton.codec.ReadableBuffer;
+import org.apache.qpid.proton.codec.WritableBuffer;
 import org.apache.qpid.proton.message.impl.MessageImpl;
 
 /**
@@ -169,6 +170,18 @@
     int decode(byte[] data, int offset, int length);
 
     /**
+     * Decodes the Message from the given {@link ReadableBuffer}.
+     * <p>
+     * If the buffer given does not contain the fully encoded Message bytes for decode
+     * this method will throw an exception to indicate the buffer underflow condition and
+     * the message object will be left in an undefined state.
+     *
+     * @param buffer
+     *      A {@link ReadableBuffer} that contains the complete message bytes.
+     */
+    void decode(ReadableBuffer buffer);
+
+    /**
      * Encodes up to {@code length} bytes of the message into the provided byte array,
      * starting at position {@code offset}.
      *
@@ -179,6 +192,21 @@
      */
     int encode(byte[] data, int offset, int length);
 
+    /**
+     * Encodes the current Message contents into the given {@link WritableBuffer} instance.
+     * <p>
+     * This method attempts to encode all message data into the {@link WritableBuffer} and
+     * if the buffer has insufficient space it will throw an exception to indicate the buffer
+     * overflow condition.  If successful the method returns the number of bytes written to
+     * the provided buffer to fully encode the message.
+     *
+     * @param buffer
+     *      The {@link WritableBuffer} instance to encode the message contents into.
+     *
+     * @return the number of bytes written to fully encode the message.
+     */
+    int encode(WritableBuffer buffer);
+
     void clear();
 
     MessageError getError();
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/message/impl/MessageImplTest.java b/proton-j/src/test/java/org/apache/qpid/proton/message/impl/MessageImplTest.java
index 6070745..0c0c003 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/message/impl/MessageImplTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/message/impl/MessageImplTest.java
@@ -27,6 +27,8 @@
 
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.messaging.Data;
+import org.apache.qpid.proton.codec.WritableBuffer;
+import org.apache.qpid.proton.codec.WritableBuffer.ByteBufferWrapper;
 import org.apache.qpid.proton.message.Message;
 import org.junit.Test;
 
@@ -62,6 +64,34 @@
         assertEquals("Encoded length different than expected length", encodedLength, encodedBytes.length);
     }
 
+    @Test
+    public void testEncodeOfMessageWithSmallDataBodyOnlyUsingWritableBuffer()
+    {
+        doMessageEncodingWithDataBodySectionTestImpl(5);
+    }
+
+    @Test
+    public void testEncodeOfMessageWithLargerDataBodyOnlyUsingWritableBuffer()
+    {
+        doMessageEncodingWithDataBodySectionTestImpl(1024);
+    }
+
+    void doMessageEncodingWithDataBodySectionTestImplUsingWritableBuffer(int bytesLength)
+    {
+        byte[] bytes = generateByteArray(bytesLength);
+
+        byte[] expectedBytes = generateExpectedDataSectionBytes(bytes);
+        ByteBufferWrapper encodedBytes = WritableBuffer.ByteBufferWrapper.allocate(expectedBytes.length);
+
+        Message msg = Message.Factory.create();
+        msg.setBody(new Data(new Binary(bytes)));
+
+        int encodedLength = msg.encode(encodedBytes);
+
+        assertArrayEquals("Encoded bytes do not match expectation", expectedBytes, encodedBytes.byteBuffer().array());
+        assertEquals("Encoded length different than expected length", encodedLength, encodedBytes.position());
+    }
+
     private byte[] generateByteArray(int bytesLength)
     {
         byte[] bytes = new byte[bytesLength];