/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.remoting3.test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.jboss.logging.Logger;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.CloseHandler;
import org.jboss.remoting3.MessageCancelledException;
import org.jboss.remoting3.MessageInputStream;
import org.jboss.remoting3.MessageOutputStream;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.wildfly.security.WildFlyElytronProvider;
import org.xnio.IoUtils;
/**
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public abstract class ChannelTestBase {
private static final int TEST_FILE_LENGTH = 20480;
protected Channel sendChannel;
protected Channel recvChannel;
private static String providerName;
@BeforeClass
public static void doBeforeClass() {
final WildFlyElytronProvider provider = new WildFlyElytronProvider();
Security.addProvider(provider);
providerName = provider.getName();
}
@AfterClass
public static void doAfterClass() {
Security.removeProvider(providerName);
}
@Rule
public TestName name = new TestName();
@Before
public void doBefore() {
System.gc();
System.runFinalization();
Logger.getLogger("TEST").infof("Running test %s", name.getMethodName());
}
@After
public void doAfter() {
System.gc();
System.runFinalization();
Logger.getLogger("TEST").infof("Finished test %s", name.getMethodName());
}
@Test
public void testEmptyMessage() throws IOException, InterruptedException {
final AtomicBoolean wasEmpty = new AtomicBoolean();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
final CountDownLatch latch = new CountDownLatch(1);
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
exRef.set(error);
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received");
try {
if (message.read() == -1) {
wasEmpty.set(true);
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
MessageOutputStream messageOutputStream = sendChannel.writeMessage();
messageOutputStream.close();
latch.await();
IOException exception = exRef.get();
if (exception != null) {
throw exception;
}
assertTrue(wasEmpty.get());
}
@Test
public void testLotsOfContent() throws IOException, InterruptedException {
final AtomicBoolean wasOk = new AtomicBoolean();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
final CountDownLatch latch = new CountDownLatch(1);
InputStream stream = ChannelTestBase.class.getResourceAsStream("/test-content.bin");
assertNotNull(stream);
final byte[] data;
try {
data = new byte[TEST_FILE_LENGTH];
int c = 0;
do {
int r = stream.read(data, c, TEST_FILE_LENGTH - c);
if (r == -1) {
break;
}
c += r;
} while (c < TEST_FILE_LENGTH);
stream.close();
} finally {
IoUtils.safeClose(stream);
}
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
exRef.set(error);
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
new Thread(new Runnable() {
public void run() {
try {
System.out.println("Message received");
final byte[] received = new byte[TEST_FILE_LENGTH];
int c = 0;
do {
int r = message.read(received, c, TEST_FILE_LENGTH - c);
if (r == -1) {
break;
}
c += r;
} while (c < TEST_FILE_LENGTH);
message.close();
assertArrayEquals(data, received);
wasOk.set(true);
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
}).start();
}
});
MessageOutputStream messageOutputStream = sendChannel.writeMessage();
messageOutputStream.write(data);
messageOutputStream.close();
messageOutputStream.close(); // close should be idempotent
messageOutputStream.flush(); // no effect expected, since message is closed
messageOutputStream.flush();
messageOutputStream.flush();
latch.await();
IOException exception = exRef.get();
if (exception != null) {
throw exception;
}
assertTrue(wasOk.get());
}
@Test
public void testWriteCancel() throws IOException, InterruptedException {
InputStream stream = ChannelTestBase.class.getResourceAsStream("/test-content.bin");
assertNotNull(stream);
final byte[] data;
try {
data = new byte[TEST_FILE_LENGTH];
int c = 0;
do {
int r = stream.read(data, c, TEST_FILE_LENGTH - c);
if (r == -1) {
break;
}
c += r;
} while (c < TEST_FILE_LENGTH);
stream.close();
} finally {
IoUtils.safeClose(stream);
}
testWriteCancel(data);
}
@Test
public void testWriteCancelIncompleteMessage() throws IOException, InterruptedException {
InputStream stream = ChannelTestBase.class.getResourceAsStream("/test-content.bin");
assertNotNull(stream);
final byte[] data;
try {
data = new byte[TEST_FILE_LENGTH/2];
int c = 0;
do {
int r = stream.read(data, c, TEST_FILE_LENGTH/2 - c);
if (r == -1) {
break;
}
c += r;
} while (c < TEST_FILE_LENGTH/2);
stream.close();
} finally {
IoUtils.safeClose(stream);
}
testWriteCancel(data);
}
public void testWriteCancel(final byte[] data) throws IOException, InterruptedException {
final AtomicBoolean wasOk = new AtomicBoolean();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
final CountDownLatch latch = new CountDownLatch(1);
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
exRef.set(error);
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
new Thread(new Runnable() {
public void run() {
final byte[] received = new byte[TEST_FILE_LENGTH];
int c = 0;
try {
System.out.println("Message received");
int r;
do {
r = message.read(received, c, TEST_FILE_LENGTH - c);
if (r == -1) {
break;
}
c += r;
} while (c < TEST_FILE_LENGTH);
if (r != -1) {
r = message.read();
}
message.close();
} catch (MessageCancelledException e) {
System.out.println("Value of c at message cancelled is " + c);
int i = 0;
while (i < c) {
if (data[i] != received[i]) {
break;
}
i++;
}
wasOk.set(i == c);
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
}).start();
}
});
MessageOutputStream messageOutputStream = sendChannel.writeMessage();
messageOutputStream.write(data);
messageOutputStream.cancel();
messageOutputStream.close();
messageOutputStream.close(); // close should be idempotent
messageOutputStream.flush(); // no effect expected, since message is closed
latch.await();
IOException exception = exRef.get();
if (exception != null) {
throw exception;
}
assertTrue(wasOk.get());
}
@Test
public void testSimpleWriteMethod() throws Exception {
Byte[] bytes = new Byte[] {1, 2, 3};
MessageOutputStream out = sendChannel.writeMessage();
for (int i = 0 ; i < bytes.length ; i++) {
out.write(bytes[i]);
}
out.close();
final CountDownLatch latch = new CountDownLatch(1);
final ArrayList<Byte> result = new ArrayList<Byte>();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received");
try {
int i = message.read();
while (i != -1) {
result.add((byte)i);
i = message.read();
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
latch.await();
assertNull(exRef.get());
Byte[] resultBytes = result.toArray(new Byte[result.size()]);
assertArrayEquals(bytes, resultBytes);
}
@Test
public void testSimpleWriteMethodWithWrappedOuputStream() throws Exception {
Byte[] bytes = new Byte[] {1, 2, 3};
FilterOutputStream out = new FilterOutputStream(sendChannel.writeMessage());
for (int i = 0 ; i < bytes.length ; i++) {
out.write(bytes[i]);
}
//The close() method of FilterOutputStream will flush the underlying output stream before closing it,
//so we end up with two messages
out.close();
final CountDownLatch latch = new CountDownLatch(1);
final ArrayList<Byte> result = new ArrayList<Byte>();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received");
try {
int i = message.read();
while (i != -1) {
result.add((byte)i);
i = message.read();
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
latch.await();
assertNull(exRef.get());
Byte[] resultBytes = result.toArray(new Byte[result.size()]);
assertArrayEquals(bytes, resultBytes);
}
@Test
public void testSimpleWriteMethodFromNonInitiatingSide() throws Exception {
Byte[] bytes = new Byte[] {1, 2, 3};
MessageOutputStream out = recvChannel.writeMessage();
for (int i = 0 ; i < bytes.length ; i++) {
out.write(bytes[i]);
}
out.close();
final CountDownLatch latch = new CountDownLatch(1);
final ArrayList<Byte> result = new ArrayList<Byte>();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
sendChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received");
try {
int i = message.read();
while (i != -1) {
result.add((byte)i);
i = message.read();
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
latch.await();
assertNull(exRef.get());
Byte[] resultBytes = result.toArray(new Byte[result.size()]);
assertArrayEquals(bytes, resultBytes);
}
@Test
public void testSimpleWriteMethodTwoWay() throws Exception {
Byte[] bytes = new Byte[] {1, 2, 3};
Byte[] manipulatedBytes = new Byte[] {2, 4, 6};
MessageOutputStream out = sendChannel.writeMessage();
for (int i = 0 ; i < bytes.length ; i++) {
out.write(bytes[i]);
}
out.close();
final CountDownLatch latch = new CountDownLatch(2);
final ArrayList<Byte> senderResult = new ArrayList<Byte>();
final ArrayList<Byte> receiverResult = new ArrayList<Byte>();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received on receiver");
try {
int i = message.read();
while (i != -1) {
receiverResult.add((byte)i);
System.out.println("read " + i);
i = message.read();
}
message.close();
MessageOutputStream out = channel.writeMessage();
try {
for (Byte b : receiverResult) {
byte send = (byte)(b * 2);
System.out.println("Sending back " + send);
out.write(send);
}
} finally {
out.close();
out.close(); // close should be idempotent
out.flush(); // no effect expected, since message is closed
}
System.out.println("Done writing");
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
sendChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received on sender");
try {
int i = message.read();
while (i != -1) {
senderResult.add((byte)i);
i = message.read();
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
}
}
});
latch.await();
assertNull(exRef.get());
Byte[] receiverBytes = receiverResult.toArray(new Byte[receiverResult.size()]);
assertArrayEquals(bytes, receiverBytes);
Byte[] senderBytes = senderResult.toArray(new Byte[senderResult.size()]);
assertArrayEquals(manipulatedBytes, senderBytes);
}
@Test
public void testSeveralWriteMessage() throws Exception {
final AtomicBoolean wasEmpty = new AtomicBoolean();
final AtomicReference<IOException> exRef = new AtomicReference<IOException>();
final CountDownLatch latch = new CountDownLatch(50);
final AtomicInteger count = new AtomicInteger();
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
error.printStackTrace();
exRef.set(error);
latch.countDown();
}
public void handleEnd(final Channel channel) {
System.out.println("End of channel");
latch.countDown();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
System.out.println("Message received");
try {
if (message.read() == -1) {
wasEmpty.set(true);
}
message.close();
} catch (IOException e) {
exRef.set(e);
} finally {
IoUtils.safeClose(message);
latch.countDown();
if (count.getAndIncrement() < 50) {
recvChannel.receiveMessage(this);
}
}
}
});
for (int i = 0 ; i < 50 ; i++) {
MessageOutputStream messageOutputStream = sendChannel.writeMessage();
messageOutputStream.close();
messageOutputStream.close(); // close should be idempotent
messageOutputStream.flush(); // no effect expected, since message is closed
}
latch.await();
IOException exception = exRef.get();
if (exception != null) {
throw exception;
}
assertTrue(wasEmpty.get());
}
@Test
public void testRemoteChannelClose() throws Exception {
final CountDownLatch closedLatch = new CountDownLatch(1);
sendChannel.addCloseHandler(new CloseHandler<Channel>() {
public void handleClose(final Channel closed, final IOException exception) {
closedLatch.countDown();
}
});
sendChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
channel.closeAsync();
}
public void handleEnd(final Channel channel) {
channel.closeAsync();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
IoUtils.safeClose(message);
}
});
recvChannel.receiveMessage(new Channel.Receiver() {
public void handleError(final Channel channel, final IOException error) {
channel.closeAsync();
}
public void handleEnd(final Channel channel) {
channel.closeAsync();
}
public void handleMessage(final Channel channel, final MessageInputStream message) {
IoUtils.safeClose(message);
}
});
sendChannel.writeShutdown();
IoUtils.safeClose(recvChannel);
System.out.println("Waiting for closed");
closedLatch.await();
System.out.println("Closed");
}
}