You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2007/03/13 13:21:37 UTC

svn commit: r517664 - in /incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid: interop/ interop/coordinator/ interop/old/ interop/testclient/ interop/testclient/testcases/ util/

Author: rgreig
Date: Tue Mar 13 05:21:36 2007
New Revision: 517664

URL: http://svn.apache.org/viewvc?view=rev&rev=517664
Log:
Skeleton of interop testing code added.

Added:
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java
Removed:
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java
    incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java Tue Mar 13 05:21:36 2007
@@ -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()
+    { }*/
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java Tue Mar 13 05:21:36 2007
@@ -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;
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java Tue Mar 13 05:21:36 2007
@@ -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. */
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java Tue Mar 13 05:21:36 2007
@@ -0,0 +1,291 @@
+/*
+ *
+ * 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.old;
+
+import java.util.Random;
+
+import javax.jms.*;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+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 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 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
+ *       interop spec in mind.
+ *
+ * @todo I've added lots of field table types in the report message, just to check if the other end can decode them
+ *       correctly. Not really the right place to test this, so remove them from {@link #sendReport()} once a better
+ *       test exists.
+ */
+public class Listener implements MessageListener
+{
+    private static Logger log = Logger.getLogger(Listener.class);
+
+    /** The default AMQ connection URL to use for tests. */
+    public static final String DEFAULT_URI = "amqp://guest:guest@default/test?brokerlist='tcp://localhost:5672'";
+
+    /** Holds the name of (routing key for) the topic to receive test messages on. */
+    public static final String CONTROL_TOPIC = "topic_control";
+
+    /** Holds the name of (routing key for) the queue to send reports to. */
+    public static final String RESPONSE_QUEUE = "response";
+
+    /** Holds the JMS Topic to receive test messages on. */
+    private final Topic _topic;
+
+    /** Holds the JMS Queue to send reports to. */
+    private final Queue _response;
+
+    /** Holds the connection to listen on. */
+    private final Connection _connection;
+
+    /** Holds the producer to send control messages on. */
+    private final MessageProducer _controller;
+
+    /** Holds the JMS session. */
+    private final javax.jms.Session _session;
+
+    /** Holds a flag to indicate that a timer has begun on the first message. Reset when report is sent. */
+    private boolean init;
+
+    /** Holds the count of messages received by this listener. */
+    private int count;
+
+    /** Used to hold the start time of the first message. */
+    private long start;
+
+    /**
+     * Creates a topic listener using the specified broker URL.
+     *
+     * @param connectionUrl The broker URL to listen on.
+     *
+     * @throws AMQException If the broker connection cannot be established.
+     * @throws URLSyntaxException If the broker URL syntax is not correct.
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    Listener(String connectionUrl) throws AMQException, JMSException, URLSyntaxException
+    {
+        log.debug("Listener(String connectionUrl = " + connectionUrl + "): called");
+
+        // Create a connection to the broker.
+        _connection = new AMQConnection(connectionUrl);
+
+        // Establish a session on the broker.
+        _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+        // Set up the destinations to listen for test and control messages on.
+        _topic = _session.createTopic(CONTROL_TOPIC);
+        _response = _session.createQueue(RESPONSE_QUEUE);
+
+        // Set this listener up to listen for incoming messages on the test topic.
+        _session.createConsumer(_topic).setMessageListener(this);
+
+        // Set up this listener with a producer to send the reports on.
+        _controller = _session.createProducer(_response);
+
+        _connection.start();
+        System.out.println("Waiting for messages...");
+    }
+
+    /**
+     * Starts a test subscriber. The broker URL must be specified as the first command line argument.
+     *
+     * @param argv The command line arguments, ignored.
+     *
+     * @todo Add command line arguments to configure all aspects of the test.
+     */
+    public static void main(String[] argv)
+    {
+        try
+        {
+            new Listener(DEFAULT_URI);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Handles all message received by this listener. Test messages are counted, report messages result in a report being sent and
+     * shutdown messages result in this listener being terminated.
+     *
+     * @param message The received message.
+     */
+    public void onMessage(Message message)
+    {
+        log.debug("public void onMessage(Message message = " + message + "): called");
+
+        // Take the start time of the first message if this is the first message.
+        if (!init)
+        {
+            start = System.nanoTime() / 1000000;
+            count = 0;
+            init = true;
+        }
+
+        try
+        {
+            // Check if the message is a control message telling this listener to shut down.
+            if (isShutdown(message))
+            {
+                log.debug("Got a shutdown message.");
+                shutdown();
+            }
+            // Check if the message is a report request message asking this listener to respond with the message count.
+            else if (isReport(message))
+            {
+                log.debug("Got a report request message.");
+
+                // Send the message count report.
+                sendReport();
+
+                // Reset the initialization flag so that the next message is considered to be the first.
+                init = false;
+            }
+            // Otherwise it is an ordinary test message, so increment the message count.
+            else
+            {
+                count++;
+            }
+        }
+        catch (JMSException e)
+        {
+            log.warn("There was a JMSException during onMessage.", e);
+        }
+    }
+
+    /**
+     * Checks a message to see if it is a termination request control message.
+     *
+     * @param m The message to check.
+     *
+     * @return <tt>true</tt> if it is a termination request control message, <tt>false</tt> otherwise.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    boolean isShutdown(Message m) throws JMSException
+    {
+        boolean result = checkTextField(m, "TYPE", "TERMINATION_REQUEST");
+
+        return result;
+    }
+
+    /**
+     * Checks a message to see if it is a report request control message.
+     *
+     * @param m The message to check.
+     *
+     * @return <tt>true</tt> if it is a report request control message, <tt>false</tt> otherwise.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    boolean isReport(Message m) throws JMSException
+    {
+        boolean result = checkTextField(m, "TYPE", "REPORT_REQUEST");
+
+        return result;
+    }
+
+    /**
+     * Checks whether or not a text field on a message has the specified value.
+     *
+     * @param m         The message to check.
+     * @param fieldName The name of the field to check.
+     * @param value     The expected value of the field to compare with.
+     *
+     * @return <tt>true</tt>If the specified field has the specified value, <tt>fals</tt> otherwise.
+     *
+     * @throws JMSException Any JMSExceptions are allowed to fall through.
+     */
+    private static boolean checkTextField(Message m, String fieldName, String value) throws JMSException
+    {
+        //log.debug("private static boolean checkTextField(Message m = " + m + ", String fieldName = " + fieldName
+        //          + ", String value = " + value + "): called");
+
+        String comp = m.getStringProperty(fieldName);
+        //log.debug("comp = " + comp);
+
+        boolean result = (comp != null) && comp.equals(value);
+        //log.debug("result = " + result);
+
+        return result;
+    }
+
+    /**
+     * Closes down the connection to the broker.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    private void shutdown() throws JMSException
+    {
+        _session.close();
+        _connection.stop();
+        _connection.close();
+    }
+
+    /**
+     * Send the report message to the response queue.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    private void sendReport() throws JMSException
+    {
+        log.debug("private void report(): called");
+
+        // Create the report message.
+        long time = ((System.nanoTime() / 1000000) - start);
+        String msg = "Received " + count + " in " + time + "ms";
+        Message message = _session.createTextMessage(msg);
+
+        // Shove some more field table types in the message just to see if the other end can handle it.
+        message.setBooleanProperty("BOOLEAN", true);
+        //message.setByteProperty("BYTE", (byte) 5);
+        message.setDoubleProperty("DOUBLE", Math.PI);
+        message.setFloatProperty("FLOAT", 1.0f);
+        message.setIntProperty("INT", 1);
+        message.setShortProperty("SHORT", (short) 1);
+        message.setLongProperty("LONG", (long) 1827361278);
+        message.setStringProperty("STRING", "hello");
+
+        // Send the report message.
+        _controller.send(message);
+        log.debug("Sent report: " + msg);
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java Tue Mar 13 05:21:36 2007
@@ -0,0 +1,244 @@
+/*
+ *
+ * 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.old;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.*;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+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 org.apache.qpid.interop.old.Listener}.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <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
+ *       interop spec in mind.
+ *
+ * @todo I've added lots of field table types in the report request message, just to check if the other end can decode
+ *       them correctly. Not really the right place to test this, so remove them from {@link #doTest()} once a better
+ *       test exists.
+ */
+public class Publisher implements MessageListener
+{
+    private static Logger log = Logger.getLogger(Publisher.class);
+
+    /** The default AMQ connection URL to use for tests. */
+    public static final String DEFAULT_URI = "amqp://guest:guest@default/test?brokerlist='tcp://localhost:5672'";
+
+    /** Holds the default test timeout for broker communications before tests give up. */
+    public static final int TIMEOUT = 3000;
+
+    /** Holds the routing key for the topic to send test messages on. */
+    public static final String CONTROL_TOPIC = "topic_control";
+
+    /** Holds the routing key for the queue to receive reports on. */
+    public static final String RESPONSE_QUEUE = "response";
+
+    /** Holds the JMS Topic to send test messages on. */
+    private final Topic _topic;
+
+    /** Holds the JMS Queue to receive reports on. */
+    private final Queue _response;
+
+    /** Holds the number of messages to send in each test run. */
+    private int numMessages;
+
+    /** A monitor used to wait for all reports to arrive back from consumers on. */
+    private CountDownLatch allReportsReceivedEvt;
+
+    /** Holds the connection to listen on. */
+    private Connection _connection;
+
+    /** Holds the channel for all test messages.*/
+    private Session _session;
+
+    /** Holds the producer to send test messages on. */
+    private MessageProducer publisher;
+
+    /**
+     * Creates a topic publisher that will send the specifed number of messages and expect the specifed number of report back from test
+     * subscribers.
+     *
+     * @param connectionUri  The broker URL.
+     * @param numMessages    The number of messages to send in each test.
+     * @param numSubscribers The number of subscribes that are expected to reply with a report.
+     */
+    Publisher(String connectionUri, int numMessages, int numSubscribers)
+       throws AMQException, JMSException, URLSyntaxException
+    {
+        log.debug("Publisher(String connectionUri = " + connectionUri + ", int numMessages = " + numMessages
+                  + ", int numSubscribers = " + numSubscribers + "): called");
+
+        // Create a connection to the broker.
+        _connection = new AMQConnection(connectionUri);
+
+        // Establish a session on the broker.
+        _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+        // Set up the destinations to send test messages and listen for reports on.
+        _topic = _session.createTopic(CONTROL_TOPIC);
+        _response = _session.createQueue(RESPONSE_QUEUE);
+
+        // Set this listener up to listen for reports on the response queue.
+        _session.createConsumer(_response).setMessageListener(this);
+
+        // Set up this listener with a producer to send the test messages and report requests on.
+        publisher = _session.createProducer(_topic);
+
+        // Keep the test parameters.
+        this.numMessages = numMessages;
+
+        // Set up a countdown to count all subscribers sending their reports.
+        allReportsReceivedEvt = new CountDownLatch(numSubscribers);
+
+        _connection.start();
+        System.out.println("Sending messages and waiting for reports...");
+    }
+
+    /**
+     * Start a test publisher. The broker URL must be specified as the first command line argument.
+     *
+     * @param argv The command line arguments, ignored.
+     *
+     * @todo Add command line arguments to configure all aspects of the test.
+     */
+    public static void main(String[] argv)
+    {
+        try
+        {
+            // Create an instance of this publisher with the command line parameters.
+            Publisher publisher = new Publisher(DEFAULT_URI, 1, 1);
+
+            // Publish the test messages.
+            publisher.doTest();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Sends the test messages and waits for all subscribers to reply with a report.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    public void doTest() throws JMSException
+    {
+        log.debug("public void DoTest(): called");
+
+        // Create a test message to send.
+        Message testMessage = _session.createTextMessage("test");
+
+        // Send the desired number of test messages.
+        for (int i = 0; i < numMessages; i++)
+        {
+            publisher.send(testMessage);
+        }
+
+        log.debug("Sent " + numMessages + " test messages.");
+
+        // Send the report request.
+        Message reportRequestMessage = _session.createTextMessage("Report request message.");
+        reportRequestMessage.setStringProperty("TYPE", "REPORT_REQUEST");
+
+        reportRequestMessage.setBooleanProperty("BOOLEAN", false);
+        //reportRequestMessage.Headers.SetByte("BYTE", 5);
+        reportRequestMessage.setDoubleProperty("DOUBLE", 3.141);
+        reportRequestMessage.setFloatProperty("FLOAT", 1.0f);
+        reportRequestMessage.setIntProperty("INT", 1);
+        reportRequestMessage.setLongProperty("LONG", 1);
+        reportRequestMessage.setStringProperty("STRING", "hello");
+        reportRequestMessage.setShortProperty("SHORT", (short) 2);
+
+        publisher.send(reportRequestMessage);
+
+        log.debug("Sent the report request message, waiting for all replies...");
+
+        // Wait until all the reports come in.
+        try
+        {
+            allReportsReceivedEvt.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        }
+        catch (InterruptedException e)
+        { }
+
+        // Check if all reports were really received or if the timeout occurred.
+        if (allReportsReceivedEvt.getCount() == 0)
+        {
+            log.debug("Got all reports.");
+        }
+        else
+        {
+            log.debug("Waiting for reports timed out, still waiting for " + allReportsReceivedEvt.getCount() + ".");
+        }
+
+        // Send the termination request.
+        Message terminationRequestMessage = _session.createTextMessage("Termination request message.");
+        terminationRequestMessage.setStringProperty("TYPE", "TERMINATION_REQUEST");
+        publisher.send(terminationRequestMessage);
+
+        log.debug("Sent the termination request message.");
+
+        // Close all message producers and consumers and the connection to the broker.
+        shutdown();
+    }
+
+    /**
+     * Handles all report messages from subscribers. This decrements the count of subscribers that are still to reply, until this becomes
+     * zero, at which time waiting threads are notified of this event.
+     *
+     * @param message The received report message.
+     */
+    public void onMessage(Message message)
+    {
+        log.debug("public void OnMessage(Message message = " + message + "): called");
+
+        // Decrement the count of expected messages and release the wait monitor when this becomes zero.
+        allReportsReceivedEvt.countDown();
+
+        if (allReportsReceivedEvt.getCount() == 0)
+        {
+            log.debug("Got reports from all subscribers.");
+        }
+    }
+
+    /**
+     * Stops the message consumers and closes the connection.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    private void shutdown() throws JMSException
+    {
+        _session.close();
+        _connection.close();
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java Tue Mar 13 05:21:36 2007
@@ -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;
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java Tue Mar 13 05:21:36 2007
@@ -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;
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java Tue Mar 13 05:21:36 2007
@@ -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.
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java Tue Mar 13 05:21:36 2007
@@ -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.
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java Tue Mar 13 05:21:36 2007
@@ -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;
+    }
+}

Added: incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java?view=auto&rev=517664
==============================================================================
--- incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java (added)
+++ incubator/qpid/trunk/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java Tue Mar 13 05:21:36 2007
@@ -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)
+        { }
+    }
+}