summaryrefslogtreecommitdiff
path: root/java/integrationtests
diff options
context:
space:
mode:
authorRobert Greig <rgreig@apache.org>2007-03-13 12:21:36 +0000
committerRobert Greig <rgreig@apache.org>2007-03-13 12:21:36 +0000
commitceab07b604f1bf1e0c45fe881d3e6f74b582e8d2 (patch)
tree10134e2f66fde65c00173bdbcab3198b0f79465d /java/integrationtests
parent7fc38d423e79ff0861e4f35f60857f676cd09735 (diff)
downloadqpid-python-ceab07b604f1bf1e0c45fe881d3e6f74b582e8d2.tar.gz
Skeleton of interop testing code added.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@517664 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/integrationtests')
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java116
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java168
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java35
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java (renamed from java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java)10
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java (renamed from java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java)4
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java95
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java213
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java75
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java133
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java146
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java167
11 files changed, 1155 insertions, 7 deletions
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
new file mode 100644
index 0000000000..12faa64528
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
@@ -0,0 +1,116 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.coordinator;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.util.ConversationHelper;
+
+/**
+ * An CoordinatingTestCase is a JUnit test case extension that knows how to coordinate test clients that take part in a
+ * test case as defined in the interop testing specification
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification).
+ *
+ * <p/>The real logic of the test cases built on top of this, is embeded in the comparison of the sender and receiver
+ * reports. An example test method might look like:
+ *
+ * <p/><pre>
+ * public void testExample()
+ * {
+ * Properties testConfig = new Properties();
+ * testConfig.add("TEST_CASE", "example");
+ * ...
+ *
+ * Report[] reports = sequenceTest(testConfig);
+ *
+ * // Compare sender and receiver reports.
+ * if (report[0] ... report[1] ...)
+ * {
+ * Assert.fail("Sender and receiver reports did not match up.");
+ * }
+ * }
+ *
+ * </pre>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Coordinate the test sequence amongst participants. <td> {@link ConversationHelper}
+ * </table>
+ */
+public abstract class CoordinatingTestCase extends TestCase
+{
+ /**
+ *
+ * @param sender The contact details of the sending client in the test.
+ * @param receiver The contact details of the sending client in the test.
+ * @param allClients The list of all possible test clients that may accept the invitation.
+ * @param testProperties The test case definition.
+ */
+ public CoordinatingTestCase(TestClientDetails sender, TestClientDetails receiver,
+ Collection<TestClientDetails> allClients, Properties testProperties)
+ { }
+
+ /**
+ * Holds a test coordinating conversation with the test clients. This is the basic implementation of the inner
+ * loop of Use Case 5. It consists of assign the test roles, begining the test and gathering the test reports
+ * from the participants.
+ *
+ * @param sender The contact details of the sending client in the test.
+ * @param receiver The contact details of the receiving client in the test.
+ * @param allParticipatingClients The list of all clients accepted the invitation.
+ * @param testProperties The test case definition.
+ *
+ * @return The test results from the senders and receivers.
+ */
+ protected Object[] sequenceTest(TestClientDetails sender, TestClientDetails receiver,
+ Collection<TestClientDetails> allParticipatingClients, Properties testProperties)
+ {
+ // Check if the sender and recevier did not accept the invite to this test.
+ {
+ // Automatically fail this combination of sender and receiver.
+ }
+
+ // Assign the sender role to the sending test client.
+
+ // Assign the receiver role the receiving client.
+
+ // Wait for the senders and receivers to confirm their roles.
+
+ // Start the test.
+
+ // Wait for the test sender to return its report.
+
+ // As the receiver for its report.
+
+ // Wait for the receiver to send its report.
+
+ return null;
+ }
+
+ /*protected void setUp()
+ { }*/
+
+ /*protected void tearDown()
+ { }*/
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
new file mode 100644
index 0000000000..535b4ae014
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
@@ -0,0 +1,168 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.coordinator;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.qpid.util.CommandLineParser;
+
+import uk.co.thebadgerset.junit.extensions.TestRunnerImprovedErrorHandling;
+
+/**
+ * <p/>Implements the coordinator client described in the interop testing specification
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). This coordinator is built on
+ * top of the JUnit testing framework.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Find out what test clients are available.
+ * <tr><td> Decorate available tests to run all available clients.
+ * <tr><td> Attach XML test result logger.
+ * <tr><td> Terminate the interop testing framework.
+ * </table>
+ */
+public class Coordinator extends TestRunnerImprovedErrorHandling
+{
+ /** Holds the URL of the broker to coordinate the tests on. */
+ String brokerUrl;
+
+ /** Holds the virtual host to coordinate the tests on. If <tt>null</tt>, then the default virtual host is used. */
+ String virtualHost;
+
+ /**
+ * Creates an interop test coordinator on the specified broker and virtual host.
+ *
+ * @param brokerUrl The URL of the broker to connect to.
+ * @param virtualHost The virtual host to run all tests on. Optional, may be <tt>null</tt>.
+ */
+ Coordinator(String brokerUrl, String virtualHost)
+ {
+ // Retain the connection parameters.
+ this.brokerUrl = brokerUrl;
+ this.virtualHost = virtualHost;
+ }
+
+ /**
+ * The entry point for the interop test coordinator. This client accepts the following command line arguments:
+ *
+ * <p/><table>
+ * <tr><td> -b <td> The broker URL. <td> Mandatory.
+ * <tr><td> -h <td> The virtual host. <td> Optional.
+ * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional.
+ * </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ // Use the command line parser to evaluate the command line.
+ CommandLineParser commandLine =
+ new CommandLineParser(new String[][]
+ {
+ { "b", "The broker URL.", "broker", "true" },
+ { "h", "The virtual host to use.", "virtual host", "false" }
+ });
+
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ Properties options = null;
+
+ try
+ {
+ options = commandLine.parseCommandLine(args);
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ // Extract the command line options.
+ String brokerUrl = options.getProperty("b");
+ String virtualHost = options.getProperty("h");
+
+ // Add all the trailing command line options (name=value pairs) to system properties. Tests may pick up
+ // overridden values from there.
+ commandLine.addCommandLineToSysProperties();
+
+ // Scan for available test cases using a classpath scanner.
+ String[] testClassNames = null;
+
+ // Create a coordinator and begin its test procedure.
+ try
+ {
+ Coordinator coordinator = new Coordinator(brokerUrl, virtualHost);
+ TestResult testResult = coordinator.start(testClassNames);
+
+ if (!testResult.wasSuccessful())
+ {
+ System.exit(FAILURE_EXIT);
+ }
+ else
+ {
+ System.exit(SUCCESS_EXIT);
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ System.exit(EXCEPTION_EXIT);
+ }
+ }
+
+ public TestResult start(String[] testClassNames) throws Exception
+ {
+ // Connect to the broker.
+
+ // Broadcast the compulsory invitation to find out what clients are available to test.
+
+ // Wait for a short time, to give test clients an opportunity to reply to the invitation.
+
+ // Retain the list of all available clients.
+
+ // Run all of the tests in the suite using JUnit.
+ TestResult result = super.start(testClassNames);
+
+ // At this point in time, all tests have completed. Broadcast the shutdown message.
+
+ return result;
+ }
+
+ /**
+ * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run
+ * in any test decorators needed to add in the configured toolkits enhanced junit functionality.
+ *
+ * @param test The test to run.
+ * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for.
+ *
+ * @return The results of the test run.
+ */
+ public TestResult doRun(Test test, boolean wait)
+ {
+ // Combine together the available test cases and test clients to produce a complete list of test case instances
+ // to run as a JUnit test suite.
+
+ return null;
+ }
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
new file mode 100644
index 0000000000..3a201b6899
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.coordinator;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+public class TestClientDetails
+{
+ /** The test clients name. */
+
+ /* The test clients unqiue sequence number. Not currently used. */
+
+ /** The routing key of the test clients control topic. */
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java
index dbd07958fd..5545f8d2dc 100644
--- a/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java
@@ -18,7 +18,7 @@
* under the License.
*
*/
-package org.apache.qpid.interop;
+package org.apache.qpid.interop.old;
import java.util.Random;
@@ -37,13 +37,13 @@ import org.apache.qpid.url.URLSyntaxException;
/**
* Listener implements the listening end of the Qpid interop tests. It is capable of being run as a standalone listener
- * that responds to the test messages send by the publishing end of the tests implemented by {@link Publisher}.
+ * that responds to the test messages send by the publishing end of the tests implemented by {@link org.apache.qpid.interop.old.Publisher}.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Count messages received on a topic. <td> {@link Publisher}
- * <tr><td> Send reports on messages received, when requested to. <td> {@link Publisher}
- * <tr><td> Shutdown, when requested to. <td> {@link Publisher}
+ * <tr><td> Count messages received on a topic. <td> {@link org.apache.qpid.interop.old.Publisher}
+ * <tr><td> Send reports on messages received, when requested to. <td> {@link org.apache.qpid.interop.old.Publisher}
+ * <tr><td> Shutdown, when requested to. <td> {@link org.apache.qpid.interop.old.Publisher}
* <tr><td>
*
* @todo This doesn't implement the interop test spec yet. Its a port of the old topic tests but has been adapted with
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java
index cab679876f..f3a545f580 100644
--- a/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java
@@ -18,7 +18,7 @@
* under the License.
*
*/
-package org.apache.qpid.interop;
+package org.apache.qpid.interop.old;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -33,7 +33,7 @@ import org.apache.qpid.url.URLSyntaxException;
/**
* Publisher is the sending end of Qpid interop tests. It is capable of being run as a standalone publisher
- * that sends test messages to the listening end of the tests implemented by {@link Listener}.
+ * that sends test messages to the listening end of the tests implemented by {@link org.apache.qpid.interop.old.Listener}.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java
new file mode 100644
index 0000000000..57726285f9
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.testclient;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+
+/**
+ * InteropClientTestCase provides an interface that classes implementing test cases from the interop testing spec
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification) should implement. Implementations
+ * must be Java beans, that is, to provide a default constructor and to implement the {@link #getName} method.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Perform test case actions.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public interface InteropClientTestCase extends MessageListener
+{
+ /** Defines the possible test case roles that an interop test case can take on. */
+ public enum Roles
+ {
+ SENDER, RECEIVER;
+ }
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName();
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException;
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receiver.
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException;
+
+ /**
+ * Performs the test case actions.
+ */
+ public void start();
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The session to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException;
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
new file mode 100644
index 0000000000..2c04a8e52b
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
@@ -0,0 +1,213 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.testclient;
+
+import java.util.Properties;
+
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+import org.apache.qpid.util.CommandLineParser;
+
+/**
+ * Implements a test client as described in the interop testing spec
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that
+ * reacts to control message sequences send by the test {@link org.apache.qpid.interop.coordinator.Coordinator}.
+ *
+ * <p/><table><caption>Messages Handled by TestClient</caption>
+ * <tr><th> Message <th> Action
+ * <tr><td> Invite(compulsory) <td> Reply with Enlist.
+ * <tr><td> Invite(test case) <td> Reply with Enlist if test case available.
+ * <tr><td> AssignRole(test case) <td> Reply with Accept Role if matches an enlisted test. Keep test parameters.
+ * <tr><td> Start <td> Send test messages defined by test parameters. Send report on messages sent.
+ * <tr><td> Status Request <td> Send report on messages received.
+ * </table>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Handle all incoming control messages. <td> {@link InteropClientTestCase}
+ * <tr><td> Configure and look up test cases by name. <td> {@link InteropClientTestCase}
+ * </table>
+ */
+public class TestClient implements MessageListener
+{
+ /** Holds the URL of the broker to run the tests on. */
+ String brokerUrl;
+
+ /** Holds the virtual host to run the tests on. If <tt>null</tt>, then the default virtual host is used. */
+ String virtualHost;
+
+ /** Defines an enumeration of the control message types and handling behaviour for each. */
+ protected enum ControlMessages implements MessageListener
+ {
+ INVITE_COMPULSORY
+ {
+ public void onMessage(Message message)
+ {
+ // Reply with the client name in an Enlist message.
+ }
+ },
+ INVITE
+ {
+ public void onMessage(Message message)
+ {
+ // Extract the test properties.
+
+ // Check if the requested test case is available.
+ {
+ // Make the requested test case the current test case.
+
+ // Reply by accepting the invite in an Enlist message.
+ }
+ }
+ },
+ ASSIGN_ROLE
+ {
+ public void onMessage(Message message)
+ {
+ // Extract the test properties.
+
+ // Reply by accepting the role in an Accept Role message.
+ }
+ },
+ START
+ {
+ public void onMessage(Message message)
+ {
+ // Start the current test case.
+
+ // Generate the report from the test case and reply with it as a Report message.
+ }
+ },
+ STATUS_REQUEST
+ {
+ public void onMessage(Message message)
+ {
+ // Generate the report from the test case and reply with it as a Report message.
+ }
+ },
+ UNKNOWN
+ {
+ public void onMessage(Message message)
+ {
+ // Log a warning about this but otherwise ignore it.
+ }
+ };
+
+ /**
+ * Handles control messages appropriately depending on the message type.
+ *
+ * @param message The incoming message to handle.
+ */
+ public abstract void onMessage(Message message);
+ }
+
+ public TestClient(String brokerUrl, String virtualHost)
+ {
+ // Retain the connection parameters.
+ this.brokerUrl = brokerUrl;
+ this.virtualHost = virtualHost;
+ }
+
+ /**
+ * The entry point for the interop test coordinator. This client accepts the following command line arguments:
+ *
+ * <p/><table>
+ * <tr><td> -b <td> The broker URL. <td> Mandatory.
+ * <tr><td> -h <td> The virtual host. <td> Optional.
+ * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional.
+ * </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ // Use the command line parser to evaluate the command line.
+ CommandLineParser commandLine =
+ new CommandLineParser(new String[][]
+ {
+ { "b", "The broker URL.", "broker", "true" },
+ { "h", "The virtual host to use.", "virtual host", "false" }
+ });
+
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ Properties options = null;
+
+ try
+ {
+ options = commandLine.parseCommandLine(args);
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ // Extract the command line options.
+ String brokerUrl = options.getProperty("b");
+ String virtualHost = options.getProperty("h");
+
+ // Add all the trailing command line options (name=value pairs) to system properties. Tests may pick up
+ // overridden values from there.
+ commandLine.addCommandLineToSysProperties();
+
+ // Create a test client and start it running.
+ TestClient client = new TestClient(brokerUrl, virtualHost);
+ client.start();
+ }
+
+ private void start()
+ {
+ // Use a class path scanner to find all the interop test case implementations.
+
+ // Create all the test case implementations and index them by the test names.
+
+ // Open a connection to communicate with the coordinator on.
+
+ // Set this up to listen for control messages.
+
+ // Create a producer to send replies with.
+ }
+
+ /**
+ * Handles all incoming control messages.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message)
+ {
+ // Delegate the message handling to the message type specific handler.
+ extractMessageType(message).onMessage(message);
+ }
+
+ /**
+ * Determines the control messsage type of incoming messages.
+ *
+ * @param message The message to determine the type of.
+ *
+ * @return The control message type of the message.
+ */
+ protected ControlMessages extractMessageType(Message message)
+ {
+ return null;
+ }
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
new file mode 100644
index 0000000000..570e4ff25c
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.testclient.testcases;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+import org.apache.qpid.interop.testclient.InteropClientTestCase;
+
+/**
+ * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness
+ * is interacting with the coordinator correctly.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Perform test case actions.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase1DummyRun implements InteropClientTestCase
+{
+ public String getName()
+ {
+ return "TC1_DummyRun";
+ }
+
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ // Test parameters don't matter, accept all invites.
+ return true;
+ }
+
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ // Do nothing, both roles are the same.
+ }
+
+ public void start()
+ {
+ // Do nothing.
+ }
+
+ public Message getReport(Session session) throws JMSException
+ {
+ // Generate a dummy report, the coordinator expects a report but doesn't care what it is.
+ return session.createTextMessage("Dummy Run, Ok.");
+ }
+
+ public void onMessage(Message message)
+ {
+ // Ignore any messages.
+ }
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java
new file mode 100644
index 0000000000..c3c05d8fd9
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java
@@ -0,0 +1,133 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.interop.testclient.testcases;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+import org.apache.qpid.interop.testclient.InteropClientTestCase;
+
+/**
+ * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the
+ * default direct exchange. Produces reports on the actual number of messages sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase2BasicP2P implements InteropClientTestCase
+{
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ return "TC2_BasicP2P";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ // All invites are acceptable.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receiver.
+ *
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ // Take note of the role to be played.
+
+ // Extract and retain the test parameters.
+
+ // Create a new connection to pass the test messages on.
+
+ // Check if the sender role is being assigned, and set up a message producer if so.
+ {
+ }
+ // Otherwise the receiver role is being assigned, so set this up to listen for messages.
+ {
+ }
+ }
+
+ /**
+ * Performs the test case actions.
+ */
+ public void start()
+ {
+ // Check that the sender role is being performed.
+ {
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The session to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ // Close the test connection.
+
+ // Generate a report message containing the count of the number of messages passed.
+
+ return null;
+ }
+
+ /**
+ * Counts incoming test messages.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ // Increment the message count.
+ }
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
new file mode 100644
index 0000000000..1dd00da53b
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
@@ -0,0 +1,146 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.util;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names
+ * that match a regular expression.
+ *
+ * <p/>In order to test whether a class implements an interface or extends a class, the class must be loaded (unless
+ * the class files were to be scanned directly). Using this collector can cause problems when it scans the classpath,
+ * because loading classes will initialize their statics, which in turn may cause undesired side effects. For this
+ * reason, the collector should always be used with a regular expression, through which the class file names are
+ * filtered, and only those that pass this filter will be tested. For example, if you define tests in classes that
+ * end with the keyword "Test" then use the regular expression "Test$" to match this.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Find all classes matching type and name pattern on the classpath.
+ * </table>
+ */
+public class ClasspathScanner
+{
+ static final int SUFFIX_LENGTH = ".class".length();
+
+ /**
+ * Scans the classpath and returns all classes that extend a specified class and match a specified name.
+ * There is an flag that can be used to indicate that only Java Beans will be matched (that is, only those classes
+ * that have a default constructor).
+ *
+ * @param matchingClass The class or interface to match.
+ * @param matchingRegexp The reular expression to match against the class name.
+ * @param beanOnly Flag to indicate that onyl classes with default constructors should be matched.
+ *
+ * @return All the classes that match this collector.
+ */
+ public static Collection<Class<?>> getMatches(Class<?> matchingClass, String matchingRegexp, boolean beanOnly)
+ {
+ String classPath = System.getProperty("java.class.path");
+ Map result = new HashMap();
+
+ for (String path : splitClassPath(classPath))
+ {
+ gatherFiles(new File(path), "", result);
+ }
+
+ return result.values();
+ }
+
+ private static void gatherFiles(File classRoot, String classFileName, Map result)
+ {
+ File thisRoot = new File(classRoot, classFileName);
+
+ if (thisRoot.isFile())
+ {
+ if (matchesName(classFileName))
+ {
+ String className = classNameFromFile(classFileName);
+ result.put(className, className);
+ }
+
+ return;
+ }
+
+ String[] contents = thisRoot.list();
+
+ if (contents != null)
+ {
+ for (String content : contents)
+ {
+ gatherFiles(classRoot, classFileName + File.separatorChar + content, result);
+ }
+ }
+ }
+
+ private static boolean matchesName(String classFileName)
+ {
+ return classFileName.endsWith(".class") && (classFileName.indexOf('$') < 0) && (classFileName.indexOf("Test") > 0);
+ }
+
+ private static boolean matchesInterface()
+ {
+ return false;
+ }
+
+ /**
+ * Takes a classpath (which is a series of paths) and splits it into its component paths.
+ *
+ * @param classPath The classpath to split.
+ *
+ * @return A list of the component paths that make up the class path.
+ */
+ private static List<String> splitClassPath(String classPath)
+ {
+ List<String> result = new LinkedList<String>();
+ String separator = System.getProperty("path.separator");
+ StringTokenizer tokenizer = new StringTokenizer(classPath, separator);
+
+ while (tokenizer.hasMoreTokens())
+ {
+ result.add(tokenizer.nextToken());
+ }
+
+ return result;
+ }
+
+ /**
+ * convert /a/b.class to a.b
+ *
+ * @param classFileName
+ *
+ * @return
+ */
+ private static String classNameFromFile(String classFileName)
+ {
+
+ String s = classFileName.substring(0, classFileName.length() - SUFFIX_LENGTH);
+ String s2 = s.replace(File.separatorChar, '.');
+ if (s2.startsWith("."))
+ {
+ return s2.substring(1);
+ }
+
+ return s2;
+ }
+}
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java
new file mode 100644
index 0000000000..bda089045a
--- /dev/null
+++ b/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java
@@ -0,0 +1,167 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.qpid.util;
+
+import java.util.Collection;
+import java.util.Queue;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+/**
+ * A conversation helper, uses a message correlation id pattern to match up sent and received messages as a conversation
+ * over JMS messaging. Incoming message traffic is divided up by correlation id. Each id has a queue (behaviour dependant
+ * on the queue implementation). Clients of this de-multiplexer can wait on messages, defined by message correlation ids.
+ * The correlating listener is a message listener, and can therefore be attached to a MessageConsumer which is consuming
+ * from a queue or topic.
+ *
+ * <p/>One use of the correlating listener is to act as a conversation synchronizer where multiple threads are carrying
+ * out conversations over a multiplexed messaging route. This can be usefull, as JMS sessions are not multi-threaded.
+ * Setting up the correlating listener with synchronous queues will allow these threads to be written in a synchronous
+ * style, but with their execution order governed by the asynchronous message flow. For example, something like the
+ * following code could run a multi-threaded conversation (the conversation methods can be called many times in
+ * parallel):
+ *
+ * <p/><pre>
+ * MessageListener conversation = new ConversationHelper(java.util.concurrent.LinkedBlockingQueue.class),
+ * sendDesitination, replyDestination);
+ *
+ * initiateConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * conversation.send(conversation.getSession().createTextMessage("Hello."));
+ * Message greeting = conversation.receive();
+ *
+ * // Exchange goodbyes.
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * Message goodbye = conversation.receive();
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ *
+ * respondToConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * Message greeting = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Hello."));
+ *
+ * // Exchange goodbyes.
+ * Message goodbye = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ *
+ * </pre>
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><th> Associate messages to a conversation using correlation ids.
+ * <tr><td> Auto manage sessions for conversations.
+ * <tr><td> Store messages not in conversation in dead letter box.
+ * </table>
+ *
+ * @todo Non-transactional, can use shared session. Transactional, must have session per-thread. Session pool? In
+ * transactional mode, commits must happen before receiving, or no replies will come in. (unless there were some
+ * pending on the queue?). Also, having received on a particular session, must ensure that session is used for all
+ * subsequent sends and receive at least until the transaction is committed. So a message selector must be used
+ * to restrict receives on that session to prevent it picking up messages bound for other conversations.
+ *
+ * @todo Want something convenient that hides many details. Write out some example use cases to get the best feel for
+ * it. Pass in connection, send destination, receive destination. Provide endConvo, send, receive
+ * methods. Bind corrId, session etc. on thread locals. Clean on endConvo. Provide deadLetter box, that
+ * uncorrelated or late messages go in. Provide time-out on wait methods, and global time-out.
+ * PingPongProducer provides a good use-case example (sends messages, waits for replies).
+ *
+ * @todo New correlationId on every send? or correlation id per conversation? or callers choice.
+ */
+public class ConversationHelper
+{
+ /**
+ * Creates a conversation helper on the specified connection with the default sending destination, and listening
+ * to the specified receiving destination.
+ *
+ * @param connection The connection to build the conversation helper on.
+ * @param sendDestination The default sending destiation for all messages.
+ * @param receiveDestination The destination to listen to for incoming messages.
+ * @param queueClass The queue implementation class.
+ */
+ public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination,
+ Class<? extends Queue> queueClass)
+ { }
+
+ /**
+ * Sends a message to the default sending location. The correlation id of the message will be assigned by this
+ * method, overriding any previously set value.
+ *
+ * @param message The message to send.
+ */
+ public void send(Message message)
+ { }
+
+ /**
+ * Gets the next message in an ongoing conversation. This method may block until such a message is received.
+ *
+ * @return The next incoming message in the conversation.
+ */
+ public Message receive()
+ {
+ return null;
+ }
+
+ /**
+ * Completes the conversation. Any open transactions are committed. Any correlation id's pertaining to the
+ * conversation are no longer valid, and any incoming messages using them will go to the dead letter box.
+ */
+ public void end()
+ { }
+
+ /**
+ * Clears the dead letter box, returning all messages that were in it.
+ *
+ * @return All messages in the dead letter box.
+ */
+ public Collection<Message> emptyDeadLetterBox()
+ {
+ return null;
+ }
+
+ /**
+ * Implements the message listener for this conversation handler.
+ */
+ protected class Receiver implements MessageListener
+ {
+ /**
+ * Handles all incoming messages in the ongoing conversations. These messages are split up by correaltion id
+ * and placed into queues.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message)
+ { }
+ }
+}