You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tr...@apache.org on 2005/11/28 17:37:20 UTC

svn commit: r349422 - in /directory/network/trunk/src: java/org/apache/mina/common/BufferDataException.java java/org/apache/mina/common/ByteBuffer.java java/org/apache/mina/common/ByteBufferProxy.java test/org/apache/mina/common/ByteBufferTest.java

Author: trustin
Date: Mon Nov 28 08:37:13 2005
New Revision: 349422

URL: http://svn.apache.org/viewcvs?rev=349422&view=rev
Log:
Resolved issues:
* DIRMINA-127 ByteBuffer.get/putPascalString()
* DIRMINA-126 ByteBuffer.get/putObject()
* DIRMINA-132 ByteBuffer.asInputStream() and ByteBuffer.asOutputStream()

Added:
    directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java   (with props)
Modified:
    directory/network/trunk/src/java/org/apache/mina/common/ByteBuffer.java
    directory/network/trunk/src/java/org/apache/mina/common/ByteBufferProxy.java
    directory/network/trunk/src/test/org/apache/mina/common/ByteBufferTest.java

Added: directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java?rev=349422&view=auto
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java (added)
+++ directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java Mon Nov 28 08:37:13 2005
@@ -0,0 +1,53 @@
+/*
+ *   @(#) $Id$
+ *
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   Licensed 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.common;
+
+/**
+ * A {@link RuntimeException} which is thrown when the data the {@link ByteBuffer}
+ * contains is corrupt.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ *
+ */
+public class BufferDataException extends RuntimeException
+{
+    private static final long serialVersionUID = -4138189188602563502L;
+
+    public BufferDataException()
+    {
+        super();
+    }
+
+    public BufferDataException( String message )
+    {
+        super( message );
+    }
+
+    public BufferDataException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public BufferDataException( Throwable cause )
+    {
+        super( cause );
+    }
+
+}

Propchange: directory/network/trunk/src/java/org/apache/mina/common/BufferDataException.java
------------------------------------------------------------------------------
    svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision

Modified: directory/network/trunk/src/java/org/apache/mina/common/ByteBuffer.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/common/ByteBuffer.java?rev=349422&r1=349421&r2=349422&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/common/ByteBuffer.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/common/ByteBuffer.java Mon Nov 28 08:37:13 2005
@@ -18,6 +18,12 @@
  */
 package org.apache.mina.common;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
 import java.nio.BufferOverflowException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteOrder;
@@ -467,6 +473,103 @@
     public abstract ByteBuffer putDouble( int index, double value );
 
     public abstract DoubleBuffer asDoubleBuffer();
+    
+    /**
+     * Returns an {@link InputStream} that reads the data from this buffer.
+     * {@link InputStream#read()} returns <tt>-1</tt> if the buffer position
+     * reaches to the limit.
+     */
+    public InputStream asInputStream()
+    {
+        return new InputStream()
+        {
+            public int available() throws IOException
+            {
+                return ByteBuffer.this.remaining();
+            }
+
+            public synchronized void mark( int readlimit )
+            {
+                ByteBuffer.this.mark();
+            }
+
+            public boolean markSupported()
+            {
+                return true;
+            }
+
+            public int read() throws IOException
+            {
+                if( ByteBuffer.this.hasRemaining() )
+                {
+                    return ByteBuffer.this.get() & 0xff;
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+
+            public int read( byte[] b, int off, int len ) throws IOException
+            {
+                int remaining = ByteBuffer.this.remaining();
+                if( remaining > 0 )
+                {
+                    int readBytes = Math.min( remaining, len );
+                    ByteBuffer.this.get( b, off, readBytes );
+                    return readBytes;
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+
+            public synchronized void reset() throws IOException
+            {
+                ByteBuffer.this.reset();
+            }
+
+            public long skip( long n ) throws IOException
+            {
+                int bytes;
+                if( n > Integer.MAX_VALUE )
+                {
+                    bytes = ByteBuffer.this.remaining();
+                }
+                else
+                {
+                    bytes = Math.min( ByteBuffer.this.remaining(), ( int ) n );
+                }
+                ByteBuffer.this.skip( bytes );
+                return bytes;
+            }
+        };
+    }
+    
+    /**
+     * Returns an {@link OutputStream} that appends the data into this buffer.
+     * Please note that the {@link OutputStream#write(int)} will throw a
+     * {@link BufferOverflowException} instead of an {@link IOException}
+     * in case of buffer overflow.  Please set <tt>autoExpand</tt> property by
+     * calling {@link #setAutoExpand(boolean)} to prevent the unexpected runtime
+     * exception.
+     */
+    public OutputStream asOutputStream()
+    {
+        return new OutputStream()
+        {
+            public void write( byte[] b, int off, int len ) throws IOException
+            {
+                ByteBuffer.this.put( b, off, len );
+            }
+
+            public void write( int b ) throws IOException
+            {
+                ByteBuffer.this.put( ( byte ) b );
+            }
+        };
+    }
 
     /**
      * Returns hexdump of this buffer.
@@ -518,6 +621,42 @@
     public abstract ByteBuffer putString(
             CharSequence in, int fieldSize, CharsetEncoder encoder ) throws CharacterCodingException;
 
+    /**
+     * Reads a Pascal string which as a 16-bit length field before the actual
+     * encoded string, using the specified <code>decoder</code> and returns it.
+     */
+    public abstract String getPascalString( CharsetDecoder decoder ) throws CharacterCodingException;
+
+    /**
+     * Writes the content of <code>in</code> into this buffer as a 
+     * Pascal string which has a 16-bit length field before the actual
+     * encoded string, using the specified <code>encoder</code>.
+     * This method doesn't terminate the string with <tt>NUL</tt>
+     * because we don't need to do so.
+     * 
+     * @throws BufferOverflowException if the specified string doesn't fit
+     */
+    public abstract ByteBuffer putPascalString( CharSequence in, CharsetEncoder encoder ) throws CharacterCodingException;
+    
+    /**
+     * Reads a Java object from the buffer using the context {@link ClassLoader}
+     * of the current thread.
+     */
+    public Object getObject() throws ClassNotFoundException
+    {
+        return getObject( Thread.currentThread().getContextClassLoader() );
+    }
+    
+    /**
+     * Reads a Java object from the buffer using the specified <tt>classLoader</tt>.
+     */
+    public abstract Object getObject( ClassLoader classLoader ) throws ClassNotFoundException;
+
+    /**
+     * Writes the specified Java object to the buffer.
+     */
+    public abstract ByteBuffer putObject( Object o );
+
     //////////////////////////
     // Skip or fill methods //
     //////////////////////////
@@ -1364,6 +1503,195 @@
             return this;
         }
         
+        public String getPascalString( CharsetDecoder decoder ) throws CharacterCodingException
+        {
+            int fieldSize = getUnsignedShort();
+            if( fieldSize < 0 )
+            {
+                throw new BufferDataException( "Invalid fieldSize: " + fieldSize );
+            }
+            
+            if( fieldSize == 0 )
+            {
+                return "";
+            }
+
+            boolean utf16 = decoder.charset().name().startsWith( "UTF-16" );
+
+            if( utf16 && ( ( fieldSize & 1 ) != 0 ) )
+            {
+                throw new BufferDataException( "fieldSize is not even for a UTF-16 string." );
+            }
+
+            int oldLimit = buf.limit();
+            int end = buf.position() + fieldSize;
+
+            if( oldLimit < end )
+            {
+                throw new BufferUnderflowException();
+            }
+
+            buf.limit( end );
+            decoder.reset();
+
+            int expectedLength = (int) ( buf.remaining() * decoder.averageCharsPerByte() );
+            CharBuffer out = CharBuffer.allocate( expectedLength );
+            for( ;; )
+            {
+                CoderResult cr;
+                if ( buf.hasRemaining() )
+                {
+                    cr = decoder.decode( buf, out, true );
+                }
+                else
+                {
+                    cr = decoder.flush( out );
+                }
+                
+                if ( cr.isUnderflow() )
+                {
+                    break;
+                }
+                
+                if ( cr.isOverflow() )
+                {
+                    CharBuffer o = CharBuffer.allocate( out.capacity() + expectedLength );
+                    out.flip();
+                    o.put(out);
+                    out = o;
+                    continue;
+                }
+
+                cr.throwException();
+            }
+            
+            buf.limit( oldLimit );
+            buf.position( end );
+            return out.flip().toString();
+        }
+        
+        public ByteBuffer putPascalString( CharSequence val, CharsetEncoder encoder ) throws CharacterCodingException
+        {
+            if( val.length() > 65535 )
+            {
+                throw new IllegalArgumentException( "The specified string is too long." );
+            }
+            if( val.length() == 0 )
+            {
+                putShort( ( short ) 0 );
+                return this;
+            }
+            
+            CharBuffer in = CharBuffer.wrap( val ); 
+            int expectedLength = (int) (in.remaining() * encoder.averageBytesPerChar());
+
+            int oldPos = position();
+            skip( 2 ); // make a room for the length field
+            encoder.reset();
+
+            for (;;) {
+                CoderResult cr;
+                if( in.hasRemaining() )
+                {
+                    cr = encoder.encode( in, buf(), true );
+                }
+                else
+                {
+                    cr = encoder.flush( buf() );
+                }
+                
+                if( position() - oldPos > 65535 )
+                {
+                    throw new IllegalArgumentException( "The specified string is too long." );
+                }
+                
+                if( cr.isUnderflow() )
+                {
+                    break;
+                }
+                if( cr.isOverflow() && autoExpand )
+                {
+                    autoExpand( expectedLength );
+                    continue;
+                }
+                cr.throwException();
+            }
+            
+            // Write the length field
+            int newPos = position();
+            position( oldPos );
+            putShort( ( short ) ( newPos - oldPos - 2 ) );
+            position( newPos );
+            return this;
+        }
+        
+        public Object getObject( final ClassLoader classLoader ) throws ClassNotFoundException
+        {
+            int length = getInt();
+            if( length > remaining() )
+            {
+                throw new BufferUnderflowException();
+            }
+            if( length <= 4 )
+            {
+                throw new BufferDataException( "Object length should be greater than 4: " + length );
+            }
+
+            int oldLimit = limit();
+            limit( position() + length );
+            try
+            {
+                ObjectInputStream in = new ObjectInputStream( asInputStream() )
+                {
+                    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
+                    {
+                        String className = readUTF();
+                        Class  clazz = Class.forName( className, true, classLoader );
+                        ObjectStreamClass descriptor = ObjectStreamClass.lookup(clazz);
+                        return descriptor;
+                    }
+                };
+                return in.readObject();
+            }
+            catch( IOException e )
+            {
+                throw new BufferDataException( e );
+            }
+            finally
+            {
+                limit( oldLimit );
+            }
+        }
+        
+        public ByteBuffer putObject( Object o )
+        {
+            int oldPos = position();
+            skip( 4 ); // Make a room for the length field.
+            try
+            {
+                ObjectOutputStream out = new ObjectOutputStream( asOutputStream() )
+                {
+                    protected void writeClassDescriptor( ObjectStreamClass desc ) throws IOException
+                    {
+                        writeUTF( desc.getName() );
+                    }
+                };
+                out.writeObject( o );
+                out.flush();
+            }
+            catch( IOException e )
+            {
+                throw new BufferDataException( e );
+            }
+            
+            // Fill the length field
+            int newPos = position();
+            position( oldPos );
+            putInt( newPos - oldPos );
+            position( newPos );
+            return this;
+        }
+
         public ByteBuffer skip( int size )
         {
             autoExpand( size );

Modified: directory/network/trunk/src/java/org/apache/mina/common/ByteBufferProxy.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/common/ByteBufferProxy.java?rev=349422&r1=349421&r2=349422&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/common/ByteBufferProxy.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/common/ByteBufferProxy.java Mon Nov 28 08:37:13 2005
@@ -18,6 +18,8 @@
  */package org.apache.mina.common;
 
 import java.io.FilterOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.ByteOrder;
 import java.nio.CharBuffer;
 import java.nio.DoubleBuffer;
@@ -450,6 +452,12 @@
     {
         return buf.getString( decoder );
     }
+    
+    public String getPascalString( CharsetDecoder decoder )
+            throws CharacterCodingException
+    {
+        return buf.getPascalString( decoder );
+    }
 
     public ByteBuffer putString( CharSequence in, int fieldSize,
                                 CharsetEncoder encoder )
@@ -465,6 +473,13 @@
         buf.putString( in, encoder );
         return this;
     }
+    
+    public ByteBuffer putPascalString( CharSequence in, CharsetEncoder encoder )
+            throws CharacterCodingException
+    {
+        buf.putPascalString( in, encoder );
+        return this;
+    }
 
     public ByteBuffer skip( int size )
     {
@@ -515,5 +530,31 @@
     public void setPooled( boolean pooled )
     {
         buf.setPooled( pooled );
+    }
+    
+    public Object getObject() throws ClassNotFoundException
+    {
+        return buf.getObject();
+    }
+
+    public Object getObject( ClassLoader classLoader ) throws ClassNotFoundException
+    {
+        return buf.getObject( classLoader );
+    }
+
+    public ByteBuffer putObject( Object o )
+    {
+        buf.putObject( o );
+        return this;
+    }
+    
+    public InputStream asInputStream()
+    {
+        return buf.asInputStream();
+    }
+    
+    public OutputStream asOutputStream()
+    {
+        return buf.asOutputStream();
     }
 }

Modified: directory/network/trunk/src/test/org/apache/mina/common/ByteBufferTest.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/test/org/apache/mina/common/ByteBufferTest.java?rev=349422&r1=349421&r2=349422&view=diff
==============================================================================
--- directory/network/trunk/src/test/org/apache/mina/common/ByteBufferTest.java (original)
+++ directory/network/trunk/src/test/org/apache/mina/common/ByteBufferTest.java Mon Nov 28 08:37:13 2005
@@ -22,6 +22,9 @@
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CharsetEncoder;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
@@ -318,5 +321,89 @@
         Assert.assertEquals( 4, buf.position() );
         Assert.assertEquals( 0, buf.get( 0 ) );
         Assert.assertEquals( 0, buf.get( 1 ) );
+    }
+    
+    public void testGetPascalString() throws Exception
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 16 );
+        CharsetEncoder encoder;
+        CharsetDecoder decoder;
+        encoder = Charset.forName( "ISO-8859-1" ).newEncoder();
+        decoder = Charset.forName( "ISO-8859-1" ).newDecoder();
+        
+        buf.putShort( ( short ) 3 );
+        buf.putString( "ABCD", encoder );
+        buf.clear();
+        Assert.assertEquals( "ABC", buf.getPascalString( decoder ) );
+    }
+    
+    public void testPutPascalString() throws Exception
+    {
+        CharsetEncoder encoder;
+        ByteBuffer buf = ByteBuffer.allocate( 16 );
+        buf.fillAndReset( buf.remaining() );
+        encoder = Charset.forName( "ISO-8859-1" ).newEncoder();
+        
+        // Without autoExpand
+        buf.putPascalString( "ABC", encoder );
+        Assert.assertEquals( 5, buf.position() );
+        Assert.assertEquals( 0, buf.get( 0 ) );
+        Assert.assertEquals( 3, buf.get( 1 ) );
+        Assert.assertEquals( 'A', buf.get( 2 ) );
+        Assert.assertEquals( 'B', buf.get( 3 ) );
+        Assert.assertEquals( 'C', buf.get( 4 ) );
+        
+        buf.clear();
+        try
+        {
+            buf.putPascalString( "123456789012345", encoder );
+            Assert.fail();
+        }
+        catch( BufferOverflowException e )
+        {
+            // OK
+        }
+        
+        // With autoExpand
+        buf.clear();
+        buf.setAutoExpand( true );
+        buf.putPascalString( "123456789012345", encoder );
+        Assert.assertEquals( 17, buf.position() );
+        Assert.assertEquals( 0, buf.get( 0 ) );
+        Assert.assertEquals( 15, buf.get( 1 ) );
+        Assert.assertEquals( '1', buf.get( 2 ) );
+        Assert.assertEquals( '2', buf.get( 3 ) );
+        Assert.assertEquals( '3', buf.get( 4 ) );
+        Assert.assertEquals( '4', buf.get( 5 ) );
+        Assert.assertEquals( '5', buf.get( 6 ) );
+        Assert.assertEquals( '6', buf.get( 7 ) );
+        Assert.assertEquals( '7', buf.get( 8 ) );
+        Assert.assertEquals( '8', buf.get( 9 ) );
+        Assert.assertEquals( '9', buf.get( 10 ) );
+        Assert.assertEquals( '0', buf.get( 11 ) );
+        Assert.assertEquals( '1', buf.get( 12 ) );
+        Assert.assertEquals( '2', buf.get( 13 ) );
+        Assert.assertEquals( '3', buf.get( 14 ) );
+        Assert.assertEquals( '4', buf.get( 15 ) );
+        Assert.assertEquals( '5', buf.get( 16 ) );
+    }
+    
+    public void testObjectSerialization() throws Exception
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 16 );
+        buf.setAutoExpand( true );
+        List o = new ArrayList();
+        o.add( new Date() );
+
+        // Test writing an object.
+        buf.putObject( o );
+
+        // Test reading an object.
+        buf.clear();
+        Object o2 = buf.getObject();
+        Assert.assertEquals( o, o2 );
+
+        // This assertion is just to make sure that deserialization occurred.
+        Assert.assertNotSame( o, o2 );
     }
 }