/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.communications.command.stream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Random;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.rhq.enterprise.communications.ServiceContainer;
import org.rhq.enterprise.communications.ServiceContainerConfigurationConstants;
import org.rhq.enterprise.communications.CommTestConstants;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import org.rhq.enterprise.communications.command.client.ClientCommandSenderConfiguration;
import org.rhq.enterprise.communications.command.client.JBossRemotingRemoteCommunicator;
import org.rhq.enterprise.communications.command.client.RemoteCommunicator;
import org.rhq.enterprise.communications.command.client.RemoteInputStream;
import org.rhq.enterprise.communications.command.client.RemoteOutputStream;
/**
* Tests remote streams. This will create two "servers" - #1 listening on one port and #2 listening on another. The
* remote pojo will be installed on server #2. Streams will be sent to/from the remote pojo on server #2. Remote streams
* (both input and output) will be hosted in both server #1 and server #2 to test sending remote streams and receiving
* remote streams.
*
* @author John Mazzitelli
*/
@Test
public class CommStreamTest {
private static final boolean ENABLE_TESTS = true;
private ClientCommandSender sender1;
private ServiceContainer serviceContainer1;
private ServiceContainer serviceContainer2;
private CommTestStreamPojo pojoImpl;
private ICommTestStreamPojo pojo;
@AfterClass
public void afterClass() {
try {
getPrefs1().removeNode();
} catch (BackingStoreException e) {
e.printStackTrace();
}
try {
getPrefs2().removeNode();
} catch (BackingStoreException e) {
e.printStackTrace();
}
}
@BeforeMethod
public void beforeMethod() throws Exception {
// setup the server #1
Preferences prefs1 = getPrefs1();
prefs1.put(ServiceContainerConfigurationConstants.CONNECTOR_TRANSPORT, "socket");
prefs1.put(ServiceContainerConfigurationConstants.CONNECTOR_BIND_ADDRESS, "127.0.0.1");
prefs1.put(ServiceContainerConfigurationConstants.CONNECTOR_BIND_PORT, CommTestConstants.CONNECTOR_BIND_PORT);
prefs1.put(ServiceContainerConfigurationConstants.CONFIG_SCHEMA_VERSION, ""
+ ServiceContainerConfigurationConstants.CURRENT_CONFIG_SCHEMA_VERSION);
prefs1.put(ServiceContainerConfigurationConstants.DATA_DIRECTORY, "target/data1");
prefs1.put(ServiceContainerConfigurationConstants.MBEANSERVER_NAME, "commstreamtest1");
prefs1.put(ServiceContainerConfigurationConstants.CMDSERVICE_DIRECTORY_DYNAMIC_DISCOVERY, "false");
serviceContainer1 = new ServiceContainer();
try {
serviceContainer1.start(prefs1, new ClientCommandSenderConfiguration());
} catch (Exception e) {
throw new Exception("Failed to bind server socket to 127.0.0.1:" + CommTestConstants.CONNECTOR_BIND_PORT, e);
}
// setup the server #2
Preferences prefs2 = getPrefs1();
prefs2.put(ServiceContainerConfigurationConstants.CONNECTOR_TRANSPORT, "socket");
prefs2.put(ServiceContainerConfigurationConstants.CONNECTOR_BIND_ADDRESS, "127.0.0.1");
prefs2.put(ServiceContainerConfigurationConstants.CONNECTOR_BIND_PORT, CommTestConstants.CONNECTOR2_BIND_PORT);
prefs2.put(ServiceContainerConfigurationConstants.CONFIG_SCHEMA_VERSION, ""
+ ServiceContainerConfigurationConstants.CURRENT_CONFIG_SCHEMA_VERSION);
prefs2.put(ServiceContainerConfigurationConstants.DATA_DIRECTORY, "target/data2");
prefs2.put(ServiceContainerConfigurationConstants.MBEANSERVER_NAME, "commstreamtest2");
prefs2.put(ServiceContainerConfigurationConstants.CMDSERVICE_DIRECTORY_DYNAMIC_DISCOVERY, "false");
serviceContainer2 = new ServiceContainer();
try {
serviceContainer2.start(prefs2, new ClientCommandSenderConfiguration());
} catch (BindException e) {
throw new Exception("Failed to bind server socket to 127.0.0.1:" + CommTestConstants.CONNECTOR2_BIND_PORT, e);
}
Thread.sleep(5000);
// install our streaming pojo in our server #2
pojoImpl = new CommTestStreamPojo(this);
serviceContainer2.addRemotePojo(pojoImpl, ICommTestStreamPojo.class);
// setup the client to server #2
RemoteCommunicator comm = new JBossRemotingRemoteCommunicator("socket://127.0.0.1:" +
CommTestConstants.CONNECTOR2_BIND_PORT + "/?force_remote=true");
ClientCommandSenderConfiguration config = new ClientCommandSenderConfiguration();
config.maxConcurrent = Integer.MAX_VALUE; // let the sender send as fast as it can
config.defaultTimeoutMillis = 60000L;
config.commandSpoolFileName = null;
config.enableQueueThrottling = false;
config.enableSendThrottling = false;
config.serverPollingIntervalMillis = 0;
sender1 = new ClientCommandSender(comm, config);
sender1.startSending();
pojo = sender1.getClientRemotePojoFactory().getRemotePojo(ICommTestStreamPojo.class);
return;
}
@AfterMethod(alwaysRun = true)
public void afterMethod() {
if (sender1 != null) {
sender1.stopSending(false);
}
if (serviceContainer1 != null) {
serviceContainer1.shutdown();
}
if (serviceContainer2 != null) {
serviceContainer2.shutdown();
}
}
@Test(enabled = ENABLE_TESTS)
public void testInputStreamReturn() throws Exception {
assert pojo.ping();
InputStream in;
int expectedLength = CommTestStreamPojo.INPUT_STREAM_STRING.getBytes().length;
byte[] bytes = new byte[expectedLength];
// try the different read methods
in = pojo.returnInputStream();
assert in instanceof RemoteInputStream;
assert expectedLength == in.read(bytes);
assert new String(bytes).equals(CommTestStreamPojo.INPUT_STREAM_STRING);
in.close();
in = pojo.returnInputStream();
assert in instanceof RemoteInputStream;
assert 2 == in.read(bytes, 0, 2);
assert bytes[0] == CommTestStreamPojo.INPUT_STREAM_STRING.getBytes()[0];
assert bytes[1] == CommTestStreamPojo.INPUT_STREAM_STRING.getBytes()[1];
in.close();
in = pojo.returnInputStream();
assert in instanceof RemoteInputStream;
assert CommTestStreamPojo.INPUT_STREAM_STRING.getBytes()[0] == in.read();
in.close();
in = pojo.returnInputStream();
assert in instanceof RemoteInputStream;
assert CommTestStreamPojo.INPUT_STREAM_STRING.getBytes().length == in.available();
in.close();
}
@Test(enabled = ENABLE_TESTS)
public void testInputStreamParam() throws Exception {
assert pojo.ping();
final String streamString = "Comm Stream Test String";
// this will throw an exception if it failed
InputStream in = prepareRemoteStreamInServer1(new ByteArrayInputStream(streamString.getBytes()));
assert in instanceof RemoteInputStream;
assert pojo.slurpInputStream(in, streamString);
}
@Test(enabled = ENABLE_TESTS)
public void testOutputStreamReturn() throws Exception {
assert pojo.ping();
OutputStream out;
String str;
// try the different write methods
out = pojo.returnOutputStream();
assert out instanceof RemoteOutputStream;
str = "first write test";
out.write(str.getBytes());
out.close();
assert str.equals(pojoImpl.byteArrayOutputStream.toString());
out = pojo.returnOutputStream();
assert out instanceof RemoteOutputStream;
str = "second write test";
out.write(str.getBytes(), 0, str.length());
out.close();
assert str.equals(pojoImpl.byteArrayOutputStream.toString());
out = pojo.returnOutputStream();
assert out instanceof RemoteOutputStream;
out.write('z');
out.close();
assert 'z' == pojoImpl.byteArrayOutputStream.toByteArray()[0];
}
@Test(enabled = ENABLE_TESTS)
public void testOutputStreamParam() throws Exception {
assert pojo.ping();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assert baos.toByteArray().length == 0;
OutputStream out = prepareRemoteStreamInServer1(baos);
assert out instanceof RemoteOutputStream;
String contentsToWrite = "remote write test!";
assert pojo.slurpOutputStream(out, contentsToWrite);
assert baos.toByteArray().length == contentsToWrite.length();
assert baos.toString().equals(contentsToWrite);
}
@Test(enabled = ENABLE_TESTS)
public void testOutputStreamPerformance() throws Exception {
assert pojo.ping();
OutputStream out;
out = pojo.returnOutputStream();
assert out instanceof RemoteOutputStream;
byte[] data = new byte[32768];
Arrays.fill(data, (byte) 0xff);
int loopCount = 100;
long start = System.currentTimeMillis();
for (int i = 0; i < loopCount; i++) {
out.write(data);
}
out.close();
long end = System.currentTimeMillis();
assert (loopCount * data.length) == pojoImpl.byteArrayOutputStream.toByteArray().length;
System.out.println("---> REMOTE OUTPUT STREAM: wrote [" + (loopCount * data.length) + "] byte in ["
+ (end - start) + "]ms");
pojoImpl.byteArrayOutputStream = null; // free up memory
// for giggles, let's see how fast true in-mem streaming it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
start = System.currentTimeMillis();
for (int i = 0; i < loopCount; i++) {
baos.write(data);
}
baos.close();
end = System.currentTimeMillis();
assert (loopCount * data.length) == baos.toByteArray().length;
System.out.println("---> NORMAL BYTE OUTPUT STREAM: wrote [" + (loopCount * data.length) + "] bytes in ["
+ (end - start) + "]ms");
baos = null;
// i wonder how fast file I/O is compared to the above
File tmpFile = File.createTempFile("commStreamTest", ".dat");
FileOutputStream fos = new FileOutputStream(tmpFile);
try {
start = System.currentTimeMillis();
for (int i = 0; i < loopCount; i++) {
fos.write(data);
}
fos.flush();
fos.close();
end = System.currentTimeMillis();
assert (loopCount * data.length) == tmpFile.length();
System.out.println("---> FILE OUTPUT STREAM: wrote [" + (loopCount * data.length) + "] bytes in ["
+ (end - start) + "]ms");
} finally {
tmpFile.delete();
}
}
@Test(enabled = ENABLE_TESTS)
public void testOutputStreamMultiRequests() throws Exception {
assert pojo.ping();
OutputStream out;
out = pojo.returnOutputStream();
assert out instanceof RemoteOutputStream;
// make sure we send the requests serialially - maxConcurrent should be forced to 1
byte loopCount = Byte.MAX_VALUE;
for (byte i = 0; i < loopCount; i++) {
out.write(i);
}
out.close();
byte[] results = pojoImpl.byteArrayOutputStream.toByteArray();
assert loopCount == results.length : "loopCount=" + loopCount + "!=results.length=" + results.length;
for (byte i = 0; i < loopCount; i++) {
assert i == results[i];
}
}
@Test(enabled = ENABLE_TESTS)
public void testSocketOutputStream() throws Exception {
assert pojo.ping();
String contentsToWrite = "remote write test!";
SocketThread thread = new SocketThread();
thread.contentsToExpect = contentsToWrite;
thread.start();
Thread.sleep(1000L);
assert thread.port != 0;
Socket socket = new Socket("127.0.0.1", thread.port);
OutputStream os = null;
try {
os = socket.getOutputStream();
assert os != null;
OutputStream out = prepareRemoteStreamInServer1(os);
assert out instanceof RemoteOutputStream;
assert pojo.slurpOutputStream(out, contentsToWrite);
os.close();
Thread.sleep(250L);
assert thread.results != null;
assert thread.results.equals(contentsToWrite);
} finally {
if (socket != null) {
socket.close();
}
if (os != null) {
os.close();
}
}
}
@Test(enabled = ENABLE_TESTS)
public void testSocketOutputStreamRange() throws Exception {
assert pojo.ping();
String contents = "0123456789abcde0123456789";
int startByte = 10;
int endByte = 14;
SocketThread thread = new SocketThread();
thread.contentsToExpect = "abcde";
thread.start();
Thread.sleep(1000L);
assert thread.port != 0;
Socket socket = new Socket("127.0.0.1", thread.port);
OutputStream os = null;
try {
os = socket.getOutputStream();
assert os != null;
OutputStream out = prepareRemoteStreamInServer1(os);
assert out instanceof RemoteOutputStream;
assert pojo.slurpOutputStreamRange(out, contents, startByte, endByte) == ((endByte - startByte) + 1);
os.close();
Thread.sleep(250L);
assert thread.results != null;
assert thread.results.equals(contents.substring(startByte, endByte + 1));
} finally {
if (socket != null) {
socket.close();
}
if (os != null) {
os.close();
}
}
}
@Test(enabled = ENABLE_TESTS)
public void testSocketOutputStreamRange2() throws Exception {
// this test's a very specific range that was seen to fail
assert pojo.ping();
String contents;
int startByte = 280;
int endByte = 7861;
// this is the length of the string we expect to copy from the stream
int expectedLength = endByte - startByte + 1;
// let's build a big test string
int bigLength = endByte * 2;
Random rand = new Random();
StringBuilder contentsBuilder = new StringBuilder(bigLength);
for (int i = 0; i < bigLength; i++) {
contentsBuilder.append((char) ('a' + rand.nextInt(26)));
}
contents = contentsBuilder.toString();
SocketThread thread = new SocketThread();
thread.contentsToExpect = contents.substring(startByte, endByte + 1);
assert thread.contentsToExpect.length() == expectedLength : "sanity check failed!?";
thread.start();
Thread.sleep(1000L);
assert thread.port != 0;
Socket socket = new Socket("127.0.0.1", thread.port);
OutputStream os = null;
try {
os = socket.getOutputStream();
assert os != null;
OutputStream out = prepareRemoteStreamInServer1(os);
assert out instanceof RemoteOutputStream;
assert pojo.slurpOutputStreamRange(out, contents, startByte, endByte) == ((endByte - startByte) + 1);
os.close();
Thread.sleep(250L);
assert thread.results != null;
assert thread.results.length() == expectedLength : "->" + thread.results.length() + ":" + expectedLength;
assert thread.results.equals(contents.substring(startByte, endByte + 1));
} finally {
if (socket != null) {
socket.close();
}
if (os != null) {
os.close();
}
}
}
private Preferences getPrefs1() {
Preferences topNode = Preferences.userRoot().node("rhq-agent");
Preferences preferencesNode = topNode.node("commstream1test");
return preferencesNode;
}
private Preferences getPrefs2() {
Preferences topNode = Preferences.userRoot().node("rhq-agent");
Preferences preferencesNode = topNode.node("commstream2test");
return preferencesNode;
}
private InputStream prepareRemoteStreamInServer1(InputStream in) throws Exception {
return new RemoteInputStream(in, serviceContainer1);
}
private OutputStream prepareRemoteStreamInServer1(OutputStream out) throws Exception {
return new RemoteOutputStream(out, serviceContainer1);
}
// package scoped so it can be used by the pojo class
InputStream prepareRemoteStreamInServer2(InputStream in) throws Exception {
return new RemoteInputStream(in, serviceContainer2);
}
// package scoped so it can be used by the pojo class
OutputStream prepareRemoteStreamInServer2(OutputStream out) throws Exception {
return new RemoteOutputStream(out, serviceContainer2);
}
private class SocketThread extends Thread {
public String contentsToExpect; // test will set this before starting thread
public String results; // public so tests can access it
public int port;
@Override
public void run() {
ServerSocket ss = null;
Socket s = null;
try {
ss = new ServerSocket(0);
port = ss.getLocalPort();
s = ss.accept();
InputStream in = s.getInputStream();
byte[] bytes = new byte[contentsToExpect.length()];
assert in.read(bytes) == contentsToExpect.length();
results = new String(bytes);
} catch (Throwable t) {
System.out.println("CANNOT CREATE/USE SERVER SOCKET FOR TESTING!!!");
t.printStackTrace();
} finally {
if (s != null) {
try {
s.close();
} catch (IOException e) {
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
}
}
}
}
}
}