/*
* @(#) $Id: StreamWriteFilterTest.java 391231 2006-04-04 06:21:55Z trustin $
*
* 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.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.MessageDigest;
import java.util.Random;
import junit.framework.TestCase;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoConnector;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.WriteFuture;
import org.apache.mina.common.IoFilter.NextFilter;
import org.apache.mina.common.IoFilter.WriteRequest;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.util.AvailablePortFinder;
import org.apache.mina.util.Queue;
import org.easymock.AbstractMatcher;
import org.easymock.MockControl;
/**
* Tests {@link StreamWriteFilter}.
*
* @author The Apache Directory Project (mina-dev@directory.apache.org)
* @version $Rev$, $Date$
*/
public class StreamWriteFilterTest extends TestCase {
MockControl mockSession;
MockControl mockNextFilter;
IoSession session;
NextFilter nextFilter;
protected void setUp() throws Exception
{
super.setUp();
/*
* Create the mocks.
*/
mockSession = MockControl.createControl( IoSession.class );
mockNextFilter = MockControl.createControl( NextFilter.class );
session = ( IoSession ) mockSession.getMock();
nextFilter = ( NextFilter ) mockNextFilter.getMock();
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( null );
}
public void testWriteEmptyStream() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
InputStream stream = new ByteArrayInputStream( new byte[ 0 ] );
WriteRequest writeRequest = new WriteRequest( stream );
/*
* Record expectations
*/
nextFilter.messageSent( session, stream );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
filter.filterWrite( nextFilter, session, writeRequest );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
assertTrue( writeRequest.getFuture().isWritten() );
}
/**
* Tests that the filter just passes objects which aren't InputStreams
* through to the next filter.
*/
public void testWriteNonStreamMessage() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
Object message = new Object();
WriteRequest writeRequest = new WriteRequest( message );
/*
* Record expectations
*/
nextFilter.filterWrite( session, writeRequest );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( null );
nextFilter.messageSent( session, message );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
filter.filterWrite( nextFilter, session, writeRequest );
filter.messageSent( nextFilter, session, message );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
assertTrue( writeRequest.getFuture().isWritten() );
}
/**
* Tests when the contents of the stream fits into one write buffer.
*/
public void testWriteSingleBufferStream() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
byte[] data = new byte[] { 1, 2, 3, 4 };
InputStream stream = new ByteArrayInputStream( data );
WriteRequest writeRequest = new WriteRequest( stream );
/*
* Record expectations
*/
session.setAttribute( StreamWriteFilter.CURRENT_STREAM, stream );
mockSession.setReturnValue(null);
session.setAttribute( StreamWriteFilter.INITIAL_WRITE_FUTURE, writeRequest.getFuture() );
mockSession.setReturnValue(null);
nextFilter.filterWrite( session, new WriteRequest( ByteBuffer.wrap( data ) ) );
mockNextFilter.setMatcher( new WriteRequestMatcher() );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.INITIAL_WRITE_FUTURE );
mockSession.setReturnValue( writeRequest.getFuture() );
session.removeAttribute( StreamWriteFilter.WRITE_REQUEST_QUEUE );
mockSession.setReturnValue( null );
nextFilter.messageSent( session, stream );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
filter.filterWrite( nextFilter, session, writeRequest );
filter.messageSent( nextFilter, session, data );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
assertTrue( writeRequest.getFuture().isWritten() );
}
/**
* Tests when the contents of the stream doesn't fit into one write buffer.
*/
public void testWriteSeveralBuffersStream() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
filter.setWriteBufferSize( 4 );
byte[] data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
byte[] chunk1 = new byte[] { 1, 2, 3, 4 };
byte[] chunk2 = new byte[] { 5, 6, 7, 8 };
byte[] chunk3 = new byte[] { 9, 10 };
InputStream stream = new ByteArrayInputStream( data );
WriteRequest writeRequest = new WriteRequest( stream );
/*
* Record expectations
*/
session.setAttribute( StreamWriteFilter.CURRENT_STREAM, stream );
mockSession.setReturnValue(null);
session.setAttribute( StreamWriteFilter.INITIAL_WRITE_FUTURE, writeRequest.getFuture() );
mockSession.setReturnValue(null);
nextFilter.filterWrite( session, new WriteRequest( ByteBuffer.wrap( chunk1 ) ) );
mockNextFilter.setMatcher( new WriteRequestMatcher() );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
nextFilter.filterWrite( session, new WriteRequest( ByteBuffer.wrap( chunk2 ) ) );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
nextFilter.filterWrite( session, new WriteRequest( ByteBuffer.wrap( chunk3 ) ) );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.INITIAL_WRITE_FUTURE );
mockSession.setReturnValue( writeRequest.getFuture() );
session.removeAttribute( StreamWriteFilter.WRITE_REQUEST_QUEUE );
mockSession.setReturnValue( null );
nextFilter.messageSent( session, stream );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
filter.filterWrite( nextFilter, session, writeRequest );
filter.messageSent( nextFilter, session, chunk1 );
filter.messageSent( nextFilter, session, chunk2 );
filter.messageSent( nextFilter, session, chunk3 );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
assertTrue( writeRequest.getFuture().isWritten() );
}
public void testWriteWhileWriteInProgress() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
Queue queue = new Queue();
InputStream stream = new ByteArrayInputStream( new byte[ 5 ] );
/*
* Record expectations
*/
mockSession.reset();
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.getAttribute( StreamWriteFilter.WRITE_REQUEST_QUEUE );
mockSession.setReturnValue( queue );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
WriteRequest wr = new WriteRequest( new Object() );
filter.filterWrite( nextFilter, session, wr );
assertEquals( 1, queue.size() );
assertSame( wr, queue.pop() );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
}
public void testWritesWriteRequestQueueWhenFinished() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
WriteRequest wrs[] = new WriteRequest[] {
new WriteRequest( new Object() ),
new WriteRequest( new Object() ),
new WriteRequest( new Object() )
};
Queue queue = new Queue();
queue.push( wrs[ 0 ] );
queue.push( wrs[ 1 ] );
queue.push( wrs[ 2 ] );
InputStream stream = new ByteArrayInputStream( new byte[ 0 ] );
/*
* Record expectations
*/
mockSession.reset();
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( stream );
session.removeAttribute( StreamWriteFilter.INITIAL_WRITE_FUTURE );
mockSession.setReturnValue( new WriteFuture() );
session.removeAttribute( StreamWriteFilter.WRITE_REQUEST_QUEUE );
mockSession.setReturnValue( queue );
nextFilter.filterWrite( session, wrs[ 0 ] );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( null );
nextFilter.filterWrite( session, wrs[ 1 ] );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( null );
nextFilter.filterWrite( session, wrs[ 2 ] );
session.getAttribute( StreamWriteFilter.CURRENT_STREAM );
mockSession.setReturnValue( null );
nextFilter.messageSent( session, stream );
/*
* Replay.
*/
mockNextFilter.replay();
mockSession.replay();
filter.messageSent( nextFilter, session, new Object() );
assertEquals( 0, queue.size() );
/*
* Verify.
*/
mockNextFilter.verify();
mockSession.verify();
}
/**
* Tests that {@link StreamWriteFilter#setWriteBufferSize(int)} checks the
* specified size.
*/
public void testSetWriteBufferSize() throws Exception
{
StreamWriteFilter filter = new StreamWriteFilter();
try
{
filter.setWriteBufferSize( 0 );
fail( "0 writeBuferSize specified. IllegalArgumentException expected." );
}
catch ( IllegalArgumentException iae )
{
}
try
{
filter.setWriteBufferSize( -100 );
fail( "Negative writeBuferSize specified. IllegalArgumentException expected." );
}
catch ( IllegalArgumentException iae )
{
}
filter.setWriteBufferSize( 1 );
assertEquals( 1, filter.getWriteBufferSize() );
filter.setWriteBufferSize( 1024 );
assertEquals( 1024, filter.getWriteBufferSize() );
}
public void testWriteUsingSocketTransport() throws Exception
{
IoAcceptor acceptor = new SocketAcceptor();
( ( SocketAcceptorConfig ) acceptor.getDefaultConfig() ).setReuseAddress( true );
SocketAddress address = new InetSocketAddress( "localhost", AvailablePortFinder.getNextAvailable() );
IoConnector connector = new SocketConnector();
FixedRandomInputStream stream = new FixedRandomInputStream( 4 * 1024 * 1024 );
SenderHandler sender = new SenderHandler( stream );
ReceiverHandler receiver = new ReceiverHandler( stream.size );
acceptor.bind( address, sender );
synchronized( sender.lock )
{
synchronized( receiver.lock )
{
connector.connect( address, receiver );
sender.lock.wait();
receiver.lock.wait();
}
}
acceptor.unbind( address );
assertEquals( stream.bytesRead, receiver.bytesRead );
assertEquals( stream.size, receiver.bytesRead );
byte[] expectedMd5 = stream.digest.digest();
byte[] actualMd5 = receiver.digest.digest();
assertEquals( expectedMd5.length, actualMd5.length );
for( int i = 0; i < expectedMd5.length; i++ )
{
assertEquals( expectedMd5[ i ], actualMd5[ i ] );
}
}
private static class FixedRandomInputStream extends InputStream
{
long size;
long bytesRead = 0;
Random random = new Random();
MessageDigest digest;
public FixedRandomInputStream( long size ) throws Exception
{
this.size = size;
digest = MessageDigest.getInstance( "MD5" );
}
public int read() throws IOException
{
if ( isAllWritten() )
return -1;
bytesRead++;
byte b = ( byte ) random.nextInt( 255 );
digest.update( b );
return b;
}
public long getBytesRead()
{
return bytesRead;
}
public long getSize()
{
return size;
}
public boolean isAllWritten()
{
return bytesRead >= size;
}
}
private static class SenderHandler extends IoHandlerAdapter
{
Object lock = new Object();
InputStream inputStream;
StreamWriteFilter streamWriteFilter = new StreamWriteFilter();
public SenderHandler( InputStream inputStream )
{
this.inputStream = inputStream;
}
public void sessionCreated( IoSession session ) throws Exception {
super.sessionCreated( session );
session.getFilterChain().addLast( "codec", streamWriteFilter );
}
public void sessionOpened( IoSession session ) throws Exception {
session.write( inputStream );
}
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
synchronized( lock )
{
lock.notifyAll();
}
}
public void sessionClosed( IoSession session ) throws Exception
{
synchronized( lock )
{
lock.notifyAll();
}
}
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
{
synchronized( lock )
{
lock.notifyAll();
}
}
public void messageSent( IoSession session, Object message ) throws Exception
{
if( message == inputStream )
{
synchronized( lock )
{
lock.notifyAll();
}
}
}
}
private static class ReceiverHandler extends IoHandlerAdapter
{
Object lock = new Object();
long bytesRead = 0;
long size = 0;
MessageDigest digest;
public ReceiverHandler( long size ) throws Exception
{
this.size = size;
digest = MessageDigest.getInstance( "MD5" );
}
public void sessionCreated( IoSession session ) throws Exception
{
super.sessionCreated(session);
session.setIdleTime( IdleStatus.READER_IDLE, 5 );
}
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
{
session.close();
}
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
synchronized( lock )
{
lock.notifyAll();
}
}
public void sessionClosed( IoSession session ) throws Exception
{
synchronized( lock )
{
lock.notifyAll();
}
}
public void messageReceived( IoSession session, Object message ) throws Exception
{
ByteBuffer buf = ( ByteBuffer ) message;
while( buf.hasRemaining() )
{
digest.update( buf.get() );
bytesRead++;
}
if( bytesRead >= size )
{
session.close();
}
}
}
public static class WriteRequestMatcher extends AbstractMatcher
{
protected boolean argumentMatches( Object expected, Object actual )
{
if( expected instanceof WriteRequest && expected instanceof WriteRequest )
{
WriteRequest w1 = ( WriteRequest ) expected;
WriteRequest w2 = ( WriteRequest ) actual;
return w1.getMessage().equals( w2.getMessage() )
&& w1.getFuture().isWritten() == w2.getFuture().isWritten();
}
return super.argumentMatches( expected, actual );
}
}
}